- Stable
3.0.0
- Canary
3.0.1-alpha.1
Toggle Menu
5.81s
40.14s
Permalinks
You can customize the default location of templates to the output directory using Eleventy’s permalinks feature.
Here’s a few examples of how it works by default (assuming your output directory is the default, _site
):
Input | index.njk |
---|---|
Output | _site/index.html |
Href | / |
Input | template.njk |
---|---|
Output | _site/template/index.html |
Href | /template/ |
Input | subdir/template.njk |
---|---|
Output | _site/subdir/template/index.html |
Href | /subdir/template/ |
Input | subdir/template/template.njk or subdir/template/index.njk |
---|---|
Output | _site/subdir/template/index.html |
Href | /subdir/template/ |
Cool URIs don’t change
Eleventy automatically helps you make sure that Cool URIs don’t change.
What to leave out… File name extension. This is a very common one. "cgi", even ".html" is something which will change. You may not be using HTML for that page in 20 years time, but you might want today's links to it to still be valid. The canonical way of making links to the W3C site doesn't use the extension.
Changing the output location
To remap your template’s output to a different path than the default, use the permalink
key in the template’s front matter. If a subdirectory does not exist, it will be created.
---
permalink: "this-is-a-new-path/subdirectory/testing/"
---
The above is functionally equivalent to:
---
permalink: "this-is-a-new-path/subdirectory/testing/index.html"
---
Both of the above examples write to _site/this-is-a-new-path/subdirectory/testing/index.html
.
Fear not: if multiple input files attempt to write to the same output location, Eleventy will throw an error for you!
index.html
is optional, it is a Common Pitfall to leave off the trailing slash! If you forget it, the browser may attempt to download the file instead of displaying it (unless you’ve done some extra work to set up your Content-Type
headers correctly).Skip writing to the file system
If you set the permalink
value to be false
, this will disable writing the file to disk in your output folder. The file will still be processed normally (and present in collections, with its url
and outputPath
properties set to false
) but will not be available in your output directory as a standalone template.
---
permalink: false
---
Use template syntax in Permalink
You may use data variables here (and template syntax, too). These will be parsed with the current template’s rendering engine. It’s recommended to use the provided slugify
filter to create URL-safe strings from data.
For example:
---
title: This is a New Path
permalink: "subdir/{{ title | slugify }}/index.html"
---
- Pagination variables also work here! Read more about Pagination
Writes to _site/subdir/this-is-a-new-path/index.html
.
page
variables useful for permalinks to see examples of this behavior: page.fileSlug
and page.filePathStem
.Expand to see another example using Liquid’s date
filter.
---
date: "2016-01-01T06:00-06:00"
permalink: "/{{ page.date | date: '%Y/%m/%d' }}/index.html"
---
Writes to _site/2016/01/01/index.html
. There are a variety of ways that the page.date variable can be set (using date
in your front matter is just one of them). Read more about Content dates.
Put quotes around template syntax in YAML
{
, for example permalink: {{ page.filePathStem }}.html
. This is a common pitfall.permalink: "{{ page.filePathStem }}.html"
The error message might look like can not read a block mapping entry; a multiline key may not be an implicit key
.
Custom File Formats
You can change the file extension in the permalink to output to any file type. For example, to generate a JSON search index to be used by popular search libraries:
---
permalink: "index.json"
---
<%- JSON.stringify(collections.all) -%>
Trailing Slashes
Eleventy projects use trailing slashes by default, as they have shown to be the most reliable approach for URL design and hosting provider compatibility. That’s why we write to /resource/index.html
and use /resource/
-style URLs.
We do offer the option to instead write /resource.html
files and use /resource
-style URLs (but it is not recommended).
Permalinks without File Extensions
While index.html
is optional on permalink: /resource/index.html
, it is a Common Pitfall to leave off the trailing slash.
If you leave off the file name and forget the trailing slash on your permalink, this will write to a file without a file extension. Your web browser may attempt to download the file instead of displaying it (unless you’ve done some extra work to set up your Content-Type
headers correctly).
This may also cause local development issues if you later attempt to write to a subdirectory of the same name (anything inside /resource/
).
permalink: /resource/
✅ Finepermalink: /resource/index.html
✅ The same as abovepermalink: /resource
❌ Not fine, throws an error in 3.0
Added in v3.0.0Eleventy will throw an error if you attempt to write to a file without a file extension. This is not always an error (think _redirects
on Netlify), so you can opt out of this feature by setting eleventyAllowMissingExtension: true
somewhere in your data cascade (front matter, directory data file, etc) or disable the error messaging globally.
---
eleventyAllowMissingExtension: true
---
export default function(eleventyConfig) {
// Disable this error for the project.
eleventyConfig.configureErrorReporting({ allowMissingExtensions: true })
};
module.exports = function(eleventyConfig) {
// Disable this error for the project.
eleventyConfig.configureErrorReporting({ allowMissingExtensions: true })
};
Remove trailing slashes
The following configuration (using global data via the configuration API but you could set this using a Global Data file too) unlocks /resource
-style URLs on your Eleventy project and works on GitHub Pages, Netlify, Cloudflare Pages, Render, and Azure Static Web Apps. This approach does not work on Vercel (due to a Vercel hosting limitation).
export default function(eleventyConfig) {
// Set global permalinks to resource.html style
eleventyConfig.addGlobalData("permalink", () => {
return (data) =>
`${data.page.filePathStem}.${data.page.outputFileExtension}`;
});
// Remove .html from `page.url` entries
eleventyConfig.addUrlTransform((page) => {
if (page.url.endsWith(".html")) {
return page.url.slice(0, -1 * ".html".length);
}
});
};
module.exports = function(eleventyConfig) {
// Set global permalinks to resource.html style
eleventyConfig.addGlobalData("permalink", () => {
return (data) =>
`${data.page.filePathStem}.${data.page.outputFileExtension}`;
});
// Remove .html from `page.url` entries
eleventyConfig.addUrlTransform((page) => {
if (page.url.endsWith(".html")) {
return page.url.slice(0, -1 * ".html".length);
}
});
};
Remove trailing slashes on Vercel
The following works for /resource
-style URLs on Vercel but additionally requires "trailingSlash": false
in your vercel.json
file.
export default function(eleventyConfig) {
eleventyConfig.addUrlTransform((page) => {
if (page.url.length !== "/" && page.url.endsWith("/")) {
return page.url.slice(0, -1);
}
});
};
module.exports = function(eleventyConfig) {
eleventyConfig.addUrlTransform((page) => {
if (page.url.length !== "/" && page.url.endsWith("/")) {
return page.url.slice(0, -1);
}
});
};
Advanced Usage
Change permalinks for one directory
Let's say you have a directory of content templates like recipes/cookies.md
and recipes/soup.md
and 50 more. Each of these content templates has a title in their frontmatter. While you could manually set a permalink in the frontmatter of each recipe you can also dynamically generate the permalink inside a Directory Data File like recipes.11tydata.js
.
Because of the order of the data cascade the title of a content template is not immediately available in the directory data file. However, permalink
is a special case of implied Computed Data and will have this data available. Inside of your directory data file recipes.11tydata.js
you could write this:
export default {
permalink: function ({ title }) {
return `/recipes/${this.slugify(title)}`;
},
};
module.exports = {
permalink: function ({ title }) {
return `/recipes/${this.slugify(title)}`;
},
};
The title will be slugified to be URL-friendly.
Mapping one URL to Multiple Files for Internationalization Added in v2.0.0
Decouple a page’s primary URL from its permalink.
As an example, say you have two content files: about.en.html
and about.es.html
. You’ve already set up the addGlobalData
feature to remap their respective output to _site/about.en.html
and _site/about.es.html
.
Use server-side redirects to control which of these files is shown.
These will work as expected out of the box, except for the page.url
variable and the URL reported in collection objects (et al).
Say we want two or more files on the file system (e.g. about.en.html
and about.es.html
) to map to a single page URL (/about/
—not or /about.en.html
). This is now possible using a new URL Transforms feature. URL transforms let you modify the /about.es.html
page.url
for a content document based.
This example matches any .xx.html
URL:
export default function (eleventyConfig) {
eleventyConfig.addUrlTransform(({ url }) => {
// `url` is guaranteed to be a string here even if you’re using `permalink: false`
if (url.match(/\.[a-z]{2}\.html$/i)) {
return url.slice(0, -1 * ".en.html".length) + "/";
}
// Returning undefined skips the url transform.
});
};
module.exports = function (eleventyConfig) {
eleventyConfig.addUrlTransform(({ url }) => {
// `url` is guaranteed to be a string here even if you’re using `permalink: false`
if (url.match(/\.[a-z]{2}\.html$/i)) {
return url.slice(0, -1 * ".en.html".length) + "/";
}
// Returning undefined skips the url transform.
});
};
Disable templating in permalinks
Some template syntaxes are nicer than others and you may want to opt-out of the templating engine here. Use the dynamicPermalink
option in your front matter to disable this on a per-template basis.
---
permalink: "/this-will-be-a-string-without-templating/"
dynamicPermalink: false
---
Globally disable templating in permalinks
Eleventy includes a global configuration option to disable dynamic templating altogether. This will save a few template renders and is probably marginally faster, too.
export default function (eleventyConfig) {
// Enabled by default
eleventyConfig.setDynamicPermalinks(false);
};
module.exports = function (eleventyConfig) {
// Enabled by default
eleventyConfig.setDynamicPermalinks(false);
};
Ignore the output directory
To remap your template’s output to a directory independent of the output directory (--output
), use permalinkBypassOutputDir: true
in your front matter.
---
permalink: "_includes/index.html"
permalinkBypassOutputDir: true
---
Writes to _includes/index.html
even though the output directory is _site
. This is useful for writing child templates to the _includes
directory for re-use in your other templates.