Skip to navigation Skip to main content

Permalinks

On this page

You can customize the default location of templates to the output directory (or disable writing a file to disk entirely) using Eleventy’s permalinks feature.

Default Output

Jump to section titled: Default Output

We’ll start with a few simple examples of the default permalink behavior, showing where input files are written to the output directory (default: _site):

index files

Jump to section titled: index files
Filename index.njk
Writes to _site/index.html
With URL /
Filename   index.liquid
Writes to {{ page.outputPath }}
With URL {{ page.url }}

Not-index files

Jump to section titled: Not-index files
Filename template.njk
Writes to _site/template/index.html
With URL /template/
Filename   template.liquid
Writes to {{ page.outputPath }}
With URL {{ page.url }}

Deeper directories

Jump to section titled: Deeper directories
Filename subdir/template.liquid
Writes to _site/subdir/template/index.html
With URL /subdir/template/
Filename   subdir/template.liquid
Writes to {{ page.outputPath }}
With URL {{ page.url }}

File name is the same as the Directory name

Jump to section titled: File name is the same as the Directory name
Filename subdir/template/template.liquid or subdir/template/index.liquid
Writes to _site/subdir/template/index.html
With URL /subdir/template/
Filename   subdir/template/template.liquid
Writes to {{ page.outputPath }}
With URL {{ page.url }}

is the same as:

Filename   subdir/template/index.liquid
Writes to {{ page.outputPath }}
With URL {{ page.url }}

Cool URIs don’t change

Jump to section titled: 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

Jump to section titled: 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 (or elsewhere in the Data Cascade). If a subdirectory does not exist, it will be created.

Syntax YAML Front Matter
---
permalink: this-is-a-new-path/subdirectory/testing/
---

The above is the same as:

Syntax YAML Front Matter
---
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!

Skip writing to the file system

Jump to section titled: Skip writing to the file system

If you set the permalink value to be false, this will disable writing the file to disk in the output folder (typically _site).

The file will still be processed and rendered for use in collections (with url and outputPath properties set to false) but will not be available in your output directory as a standalone file.

Syntax YAML Front Matter
---
permalink: false
---
Jump to section titled: 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:

Syntax YAML Front Matter using Liquid or Nunjucks
---
title: This is a New Path
permalink: "subdir/{{ title | slugify }}/index.html"
---

Writes to _site/subdir/this-is-a-new-path/index.html.

INFO:
Using the data cascade you have the power to change the default behavior for permalinks for all content in your project. Learn more about the special page variables useful for permalinks to see examples of this behavior: page.fileSlug and page.filePathStem.

Here’s another example using Liquid’s date filter:

Syntax YAML Front Matter using Liquid
---
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

Jump to section titled: Put quotes around template syntax in YAML
WARNING:
YAML Pitfall: If your permalink uses template syntax, make sure that you use quotes! Without quotes YAML may try to parse this as an object if the first character is a {, for example permalink: {{ page.filePathStem }}.html. This is a common pitfall.
Syntax YAML Front Matter using Liquid
---
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 or bad indentation of a mapping entry.

If you run the following code you’ll see the error:

---
permalink: {{ page.filePathStem }}.html
---

Custom File Formats

Jump to section titled: Custom File Formats

You can change the permalink to output to any file extension! For example:

---
permalink: index.json
---
{{ page | json: 2 }}

Trailing Slashes

Jump to section titled: 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).

Jump to section titled: Permalinks without File Extensions
WARNING:

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/).

---
# ✅ OK
permalink: /resource/

---
---
# ✅ OK (same as above)
permalink: /resource/index.html

---
---
# ⛔️ Throws an error.
permalink: /resource

---

Allow missing file extensions using Data Cascade

Jump to section titled: Allow missing file extensions using Data Cascade

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.

---
# ⚠️ Careful!
eleventyAllowMissingExtension: true
permalink: /resource

---

Allow missing file extensions globally using Configuration

Jump to section titled: Allow missing file extensions globally using Configuration
eleventy.config.js
---
# ⚠️ Careful!
permalink: /resource

---

Remove trailing slashes

Jump to section titled: 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).

eleventy.config.js

Try out the above configuration with the following template:

{{ page.url }}

Compared to the default behavior:

{{ page.url }}

Remove trailing slashes on Vercel

Jump to section titled: Remove trailing slashes on Vercel

The following works for /resource-style URLs on Vercel but additionally requires "trailingSlash": false in your vercel.json file.

eleventy.config.js
export default function(eleventyConfig) {
eleventyConfig.addUrlTransform((page) => {
// remove trailing slash from `page.url`
if (page.url !== "/" && page.url.endsWith("/")) {
return page.url.slice(0, -1);
}
});
};
module.exports = function(eleventyConfig) {
eleventyConfig.addUrlTransform((page) => {
// remove trailing slash from `page.url`
if (page.url !== "/" && page.url.endsWith("/")) {
return page.url.slice(0, -1);
}
});
};

Advanced Usage

Jump to section titled: Advanced Usage Jump to section titled: 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:

recipes.11tydata.js
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

Jump to section titled: 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 /about.en.html or /about.es.html). This is now possible using a new URL Transforms feature. URL transforms let you modify the page.url for a content document based.

This example matches any .xx.html URL:

eleventy.config.js
---
permalink: about.es.html
---

{{ page.url }}
Jump to section titled: Disable templating in permalinks

Some template syntaxes are nicer than others and you may want to opt-out of the templating engine to render your permalink string. Use the dynamicPermalink option in your front matter to disable this on a per-template basis.

WARNING:
This is a common pitfall for users of the Pug templating engine.
Syntax YAML Front Matter
---
permalink: "/this-will-be-a-string-without-{{templating}}/"
dynamicPermalink: false
---
Jump to section titled: 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.

eleventy.config.js
---
permalink: "/this-will-be-a-string-without-{{templating}}/"
---

Ignore the output directory

Jump to section titled: 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.

Syntax YAML 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.


Other pages in Configure Templates with Data