10

Managing Static Assets

When it comes to managing static assets, SvelteKit has little to do with the process. In fact, the entire process is handed over to the bundling tool Vite. By leveraging Vite for the management of static assets, we as developers don’t have to learn yet another framework-specific strategy. Instead, we can lean on Vite’s highly performant bundling and build processes. Because Vite automatically manages imported assets, we don’t have to worry about hashing files for caches. In this chapter, we’ll look at how we can leverage Vite to manage static assets such as images, fonts, audio, video, and CSS files. Once we examine how this is done, we’ll discuss some of the finer points surrounding static assets.

This chapter will be divided into the following sections:

  • Importing Assets
  • Additional Information

By the time we’re done, we’ll have a firm grasp of the best practices to use when including files that can be served as is within SvelteKit applications.

Technical requirements

The complete code for this chapter is available on GitHub at: https://github.com/PacktPublishing/SvelteKit-Up-and-Running/tree/main/chapters/chapter10.

Importing Assets

If you have worked in web development for the past decade, then you’ll remember a time when styles were written either inline or in a Cascading Style Sheet (CSS). These CSS files are helpful for creating a consistent look and feel for an application. Of course, their centralized nature comes with its own drawbacks. They often become large and difficult to navigate, which can lead to the inclusion of unused style rules. When precious milliseconds can mean the difference between converting a user or losing a sale, it’s important not to ship unused assets to clients. Besides, if we’re building a web application with SvelteKit, we really should use the Svelte approach and keep styles isolated within each component. But there are times when it’s useful to keep a style sheet that applies some global styles. For instance, imagine having to apply a specific style to each and every paragraph element. Incorporating the same simple style rule into every component across the application could lead to repetitive code. There may even be instances where we forget to include a rule, leading to inconsistent styles across the app. And while projects such as Tailwind CSS or Bootstrap are wonderful, they may not be appropriate for every project, which is why we’re going to cover how to include a global style sheet in our SvelteKit application.

To begin, we’ll need some styles. Keep in mind that these styles will apply to the entirety of the application. Normally, when creating styles within Svelte components, those styles are isolated to that specific component, meaning they are not applied to parent or child components. Many modern browsers apply their own default styles to HTML elements, and so, to unify the experience of an application, it’s common practice to create a reset.css file. This file ensures the experience is consistent across different browsers by resetting the styles applied by browsers of common elements to something predictable. For our example, we’ll use a slightly modified version of the concise yet thorough Custom CSS Reset by Josh W. Comeau. See the resources at the end of this chapter for links to the article explaining exactly how it works:

src/reset.css

/* https://www.joshwcomeau.com/css/custom-css-reset/ */
*, *::before, *::after {
  box-sizing: border-box;
}
* {
  margin: 0;
}
html, body {
  height: 100%;
  font-family: sans-serif;
}
body {
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
  display: block;
  max-width: 100%;
}
input, button, textarea, select {
  font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
  overflow-wrap: break-word;
}

In essence, the rules of this CSS file are setting more sane and predictable default styles for various HTML elements. For instance, box-sizing is set to border-box, which applies to all elements, as well as the ::before and ::after pseudo-elements. This rule means that the padding of elements will be included when calculating that element’s width. These CSS rules allow a consistent and reliable experience across browsers. Of course, we’re free to make any additions to this CSS. To make our changes slightly more noticeable, we’ve also set font-family: sans-serif; on both the html and body elements.

To include this CSS in our application, we’ll open src/routes/+layout.svelte and import it just as we would a JS module. If you remember back to Chapter 2, we used the same method to import an image path!

src/routes/+layout.svelte

<script>
  import '/src/reset.css';
  import Nav from '$lib/Nav.svelte';
  import Notify from '$lib/Notify.svelte';
  export let data;
</script>
...

Noticeably, the only change we need to make in this file is the very first line where we import reset.css. The remaining code has been omitted from the file for the sake of brevity. After importing the CSS file, notice that our styles are immediately applied. We don’t need to create <link> or <style> tags as Vite recognizes the style sheet for what it is and automatically applies it for us. Conveniently, the file import path can be relative or absolute as Vite makes no distinction between the two.

To highlight the benefits of importing a stylesheet with this method, let’s compare it with another method for including global style sheets. This method was applied to pre-1.0 releases of SvelteKit and worked by manually adding a <link> tag to the <head> section of src/app.html. It then referenced a file within the static/ directory using the %sveltekit.assets% placeholder. This method is ill-advised but let’s analyze it to consider its faults:

src/app.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%sveltekit.assets%/favicon.png" />
    <meta name="viewport" content="width=device-width" />
    <link rel="stylesheet" href="%sveltekit.assets%/global.css" />
      %sveltekit.head%
  </head>
  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">%sveltekit.body%</div>
  </body>
</html>

As we can see, this method includes the static/global.css file inside the head tag of src/app.html, the application entry point. The app.html file works as scaffolding for the rest of the application to build off of, so it stands to reason that we could include any extraneous scripts or external assets here, just as the favicon is included. This method relies on the %sveltekit.assets% placeholder to include the CSS file from the static/ directory. However, this method fails to consider Vite’s HMR features. Whenever changes are made to static/global.css, the entire development server will need to be restarted to reflect those changes as Vite does not process any of the files included in the static assets. Also consider the common scenario of applying minification to .css and preprocessing to .scss, .sass, or .less files. In each of these instances, we would need Vite to take a more hands-on approach than it does for files included from SvelteKit’s static/ directory. And because Vite can manage cached files by appending hashes to the filenames of static assets, it is clear that importing files just as we would import a JS module is in our best interest.

In Chapter 2, we saw how we could import an image URL. We’ve now also seen how Vite allows us to import a global CSS file directly into our Svelte components. Now that we’ve shown how to best utilize static assets dynamically with our applications, let’s discuss some more of the details surrounding this process.

Additional Information

We now know how we can import static files, but there are a few details to keep in mind when doing so. Here’s a breakdown of the various items we still need to cover:

  • Images versus Styles
  • Customizing Imports
  • File Paths
  • SvelteKit Configuration Options
  • Vite Configuration Options

Now let’s go over some important information about what went on behind the scenes with each of our imports.

Images versus Styles

When we imported an image in Chapter 2, we received the URL, which we then referenced in the src attribute of an <img> tag. When we imported the CSS file, we only needed to import it to apply the styles. This is because Vite is pre-configured to automatically inject the styles from CSS files into the component performing the import. Hence why the import was performed in the root +layout.svelte file. Vite also supports CSS @import and url() statements as well as CSS modules. CSS modules can be useful for importing style rules as objects within code whereas @import and url() allow developers to build a central CSS file that can reference smaller CSS files located elsewhere. When importing a CSS file, no other action needs to be taken other than the import. When importing other media such as fonts, audio, or video files, we’ll need to set the imported asset URL as the src attribute on the appropriate HTML element.

Customizing Imports

When importing static assets from Vite, we can customize how they are imported by appending the appropriate suffix to the file import names. For instance, to import a file as a string, we can append ?raw to the filename. As expected, this will give us the raw content of the imported file. For the reset.css example shown earlier, it could be included via import reset from '/src/reset.css?raw'; where the reset variable contains the content from reset.css. We would then need to find a way to include that content inside of <style> tags. In a similar fashion, if we want to import a file as a URL that is not found in the standard media types, we can append ?url to the file import statement. This can be helpful for including files served from a Content Delivery Network (CDN). We can even import scripts as web workers by appending ?worker to the filename!

File Paths

When running Vite’s development server, we can observe the network requests in our browser’s developer tools and notice that imported files are served from their location within the project source code. For example, the image from Chapter 2 is located at src/lib/images/demo.svg and is also served from that very same location. However, when we run npm run build followed by npm run preview, we’ll observe that the path has changed. It is given the path _app/immutable/assets/demo.dd76856a.svg. This path is specific to the built SvelteKit application and, normally, we won’t edit it after the application has been built. But take a moment to notice that a hash has been included in the filename. Should the file contents change, we’ll notice the built asset will include a different hash appended to the file’s name.

We can also take this moment to observe the included favicon file. We’ll see that in both dev and build/preview, it is served from the domain root directory /. This is because it was located in the static/ directory and Vite serves and builds the application so that any files located there will be served from the root level of the application.

SvelteKit Configuration Options

These options can be customized in svelte.config.js:

  • files.assets – This option specifies the directory for which static assets will be stored. SvelteKit automatically sets this option to static and overrides the Vite sibling setting specified as publicDir (which defaults to public). Files that normally fit here are robots.txt or favicon.ico as they rarely change. To reference files located here in the source code, simply prefix the filename with /. For instance, we can show the default favicon by adding <img src='/favicon.png' /> to any component.
  • paths.assets – This option takes a string that specifies the absolute path from where application files are served. It defaults to an empty string.
  • paths.base – This option also defaults to an empty string. If your application is being served from a sub-directory, you can specify the root-relative path here. Then, you may use the base module imported from $app/paths to modify hardcoded paths appropriately.
  • paths.relative – This option accepts a Boolean value. When true, the values provided by base and assets from the $app/paths module will be relative to built assets. When false, those same values with be root-relative.

Vite Configuration Options

This option can be customized in a project’s vite.config.js:

  • assetsInclude – Many common media types are automatically handled by Vite but this option can be useful if a project needs to extend the default list to treat uncommon file types as assets. This option allows for the customization of allowable static asset file types. It can be a string, regular expression, or picomatch pattern.

We’ve just seen how we can customize the importing of static assets. If we need to force an asset to be imported as a URL, we know that we append ?url to the end of the imported file. We’ve also learned how CSS files are automatically injected into the component they are imported to. Along with a few configuration options, these details provide insight into how we can make the management of static assets stress-free in our SvelteKit applications.

Summary

When including images, CSS files, or other media types in SvelteKit applications, it is clear that we should leverage Vite to import the asset just as we would import a JS module. Doing so comes with the advantage of being simple but also allowing for optimized caches. It keeps our development experience smooth as Vite’s HMR will automatically show changes in the browser. It’s also flexible in that it allows for various media types to be imported either by URL or raw content.

Now that we know how to manage static assets, we should circle back to how we manage secrets. If you recall the previous chapter, we added a personal access token to the .env file, which allowed us to authenticate with the GitHub API. In the next chapter, we’ll explore this further and cover the various modules that make managing secrets a breeze.

Resources

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset