Asset management
On this page
OverviewIn an 11ty site, you're free to structure your project however you like. You can put your static assets in a public folder outside your 11ty input directory, an assets folder in your input directory, a banana folder in your _includes... You get the idea. As long as you tell 11ty how and where to copy assets to the build output, you're good to go.
Now that Slinkity brings Vite into the equation, the days of manual passthrough copying are no more 😮 Let's learn how this works.
What gets included in my production build?
Section titled "What gets included in my production build?"✅ Resources like CSS, images, fonts, etc. will be included in the build if they are referenced by an HTML page using:
- A relative path to an asset,
- An absolute path to a passthrough-copied asset, or
- An import alias path.
For example, suppose we have a project structure that looks like this:
├── fonts
│ └── Atkinson-Hyperlegible-Regular.woff2
├── src
│ ├── _includes
│ │ └── layout.njk
│ └── index.md
└── styles
└── index.scss
We want to reference our font from that styles/index.scss.
We can use relative paths:
/* styles/index.scss */
@font-face {
font-family: Atkinson;
src: url('../fonts/Atkinson-Hyperlegible-Regular.woff2');
}
Leverage import aliases:
/* styles/index.scss */
@font-face {
font-family: Atkinson;
src: url('/@root/fonts/Atkinson-Hyperlegible-Regular.woff2');
}
Or use an absolute URL + a passthrough copy on our fonts directory:
/* styles/index.scss */
@font-face {
font-family: Atkinson;
src: url('/fonts/Atkinson-Hyperlegible-Regular.woff2');
}
// eleventy.js
module.exports = function(eleventyConfig) {
// see 11ty's passthrough copy docs for more: https://www.11ty.dev/docs/copy/
eleventyConfig.addPassthroughCopy('fonts')
}
As long as this stylesheet is later referenced somewhere in your layouts, Vite will handle the rest for you:
<!-- src/_includes/layout.njk -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- other tags omitted for brevity -->
<link rel="stylesheet" href="/@root/styles/index.scss">
</head>
<body>{{ content | safe }}</body>
</html>
⚠️ One downside to relative paths and import aliases: Vite can't resolve relative paths or import aliases from
preloadtags. If you need these for a particular resource, we recommend absolute URLs + passthrough copying.
What gets stripped from my production build?
Section titled "What gets stripped from my production build?"❌ If a resource is not referenced by an HTML page in any way (no link, image src, script src etc), it will be stripped from the build by default. This is because Vite ignores anything that's 1. not an HTML file and 2. not loaded into any other HTML file.
There are two situations where you may encounter this pitfall:
- Permalinked files that aren't HTML. Example: a
sitemap.njkpermalinked to asitemap.xml. - Non-HTML resources that aren't referenced in templates using relative paths or import aliases. Example: a passthrough-copied OpenGraph image.
This is where the public/ directory comes in!
The public/ directory
Section titled "The public/ directory" In short, anything nested inside a public/ directory is off-limits for Vite to strip. You can also treat it as a "disappearing" directory since the directory itself gets flattened in the build output. In other words, _site/public/robots.txt simply becomes _site/robots.txt without any further processing.
See Vite's documentation for more details and configuration options.
Scenario 1: Permalinked files
Section titled "Scenario 1: Permalinked files"Suppose you're using 11ty to auto-generate a sitemap.xml for your site. In 11ty, you'd create this file using your chosen templating language extension and set a root-relative permalink in its front matter, like so:
<!-- src/sitemap.liquid -->
---
permalink: /sitemap.xml
---
⚠️ This won't work in Slinkity production builds! Remember that Vite will post-process everything built by 11ty. In this example, Vite will:
- Process the 11ty build directory.
- See that a)
sitemap.xmlis not referenced by any other file and b) isn't in the dedicatedpublicfolder. - Exclude it from the final output that it writes to
_site.
Solution
To fix this problem, you need to prefix the static file's permalink with /public:
<!-- src/sitemap.liquid -->
---
permalink: /public/sitemap.xml
---
Now this happens:
- 11ty processes the template and writes it to a
publicfolder:_site/public/sitemap.xml. - Vite sees this
publicfolder and copies it into your final build output directory, giving you_site/sitemap.xml. Note that the nested/publicdirectory disappears from the final build output!
Scenario 2: Passthrough-copied files
Section titled "Scenario 2: Passthrough-copied files"Now, say we want to add an OpenGraph image to our site to get some nice social media previews. Maybe those images are stored under an assets directory:
├── assets
│ └── og-thumbnail.jpg
├── src
│ └── ...
Unfortunately, OpenGraph images need to be absolute URLs, like this:
<meta property="og:image" content="https://my-awesome-site.com/assets/og-thumbnail.jpg">
Because we're no longer using relative paths or import aliases when referencing this image, Vite won't correctly identify it as a dependency and copy it over to the build output folder. Moreover, as described before, passthrough-copying assets directly will not preserve the thumbnail.
Solution
To fix this, we'll need to nest our assets under the special public directory:
├── public
│ └── assets
│ └── og-thumbnail.jpg
├── src
│ └── ...
... and then update our 11ty config to passthrough-copy this public directory instead of assets:
// eleventy.js
module.exports = function(eleventyConfig) {
// see 11ty's passthrough copy docs for more: https://www.11ty.dev/docs/copy/
eleventyConfig.addPassthroughCopy('public')
}
Note that the public directory should be at the root level of your project.