eleventy: Slug filter doesn't create url safe slugs.
I’ve been using the slug
filter to convert regular strings to strings suitable for use as urls. However I’ve realised it’s not set up to remove apostrophes (and possibly other characters?).
So it's a test
becomes it's-a-test
, and when rendered with eleventy the url is /tags/it's-a-test/
- the ampersand breaks the url. It also looks ugly!
I see that the package supports supplying a list of characters to remove.
If the intention is that this filter is used to create ‘safe’ urls, could Eleventy remove these by default?
As an alternative I’ve added my own filter using the slugify()
option of the string library. I might stick with this anyway as it seems to do a good job of creating ‘pretty’ slugs.
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 3
- Comments: 53 (36 by maintainers)
Commits related to this issue
- fix site build with complex tags by slugifying If a 'tag' has an apostrophe in the name it breaks Netlify's deploy script, which doesn't like special characters. The built-in 'slug' function in Eleve... — committed to singaround/songbook by deleted user 5 years ago
- Fixes https://github.com/11ty/eleventy/issues/278 — committed to 11ty/eleventy by zachleat 3 years ago
yup @zachleat , it does replace the slug, so I just do remove some chars to make it little bit prettier tho
I think this change is required (I would recommend @sindresorhus/slugify, stable and well maintained) to make people’s life much easier in the future, with probably many more users than today.
One option to deal with it would be to release a 0.12.0 version with some deprecation/breaking change warnings (not only the slug, I think there are other changes) in the console, before a 1.0 version that really changes these.
This 0.12.0 version could already includes the new
slugify
function, just to make sure the warnings are shown only if the result is different from the current one.For anybody else curious, here’s the difference between the default
slug
filter (which uses slugify; currently slugify@1.5.3 in Eleventy 0.12) vs @sindresorhus/slugify@1:slug
filter (via slugify@1.5.3)slugify
filter (via @sindresorhus/slugify@1.1.2){{ Ä-ä | slug }}
= “a-a”{{ Ä-ä | slugify }}
= “ae-ae”{{ Ö-ö | slug }}
= “o-o”{{ Ö-ö | slugify }}
= “oe-oe”{{ Ü-ü | slug }}
= “u-u”{{ Ü-ü | slugify }}
= “ue-ue”{{ ẞ-ß | slug }}
= “ss-ss”{{ ẞ-ß | slugify }}
= “ss-ss”Or, if you want to replace the built-in Eleventy “slug” filter with a different implementation, just name your custom slugify filter “slug”:
NOTE: Only
@sindresorhus/slugify
v1 is supported (<kbd>npm i @sindresorhus/slugify@1</kbd>). v2 uses ESM modules and you’ll get errors when trying to use it:UPDATE: For my own future reference, I managed to get async filters working in Nunjucks using
addNunjucksAsyncFilter()
(async filter support wasn’t added into LiquidJS until v9.1.3, per https://github.com/harttle/liquidjs/issues/232) and the v2/ESM version of @sindresorhus/slugify:If you’re using Eleventy v1/Canary (which currently has liquidjs@9.25.0), this code works for an async LiquidJS filter or an .11ty.js template, but will return a Promise object in Nunjucks:
slug={{ "Peter deHaan 'tis an idiot (LiquidJS)" | slugify }}
slug=peter-de-haan-tis-an-idiot-liquid-js
slug=${ await this.slugify("Peter deHaan 'tis an idiot (11ty.js)") }
slug=peter-de-haan-tis-an-idiot-11ty-js
slug={{ "Peter deHaan 'tis an idiot (Nunjucks)" | slugify }}
slug=[object Promise]
@bridgestew are you using
eleventy-base-blog
?If so your
opts
defined here (docs: https://github.com/valeriangalliat/markdown-it-anchor): https://github.com/11ty/eleventy-base-blog/blob/master/.eleventy.js#L43would be something like:
Does that make sense?
I’m going to move this into the new feature queue and it is logged for the next major version milestone.
This PR is merged and
slugify
will ship with 1.0, thanks y’all!@Miosame you’re right, it might be enough.
I just wish the defaults would be more safe, but Eleventy can make it so.
Breaking changes should only be added on major version bumps.
11ty has 17k npm installs per week. You can only imagine the mad mob that would show up here if the slug function changed and broke their projects due to a
npm install
.Perhaps it should be “fixed” on
v1.0.0
I appreciate the pushback!
To be clear here the
slug
filter using theslugify
package is only a user-land filter and is not used in any way internally in core—I feel like some of the alternatives being proposed here have assumed that it was used internally.Exposing a new name and a soft-default-change (docs update) to point to this new name has a bunch of benefits:
slug
do not break. This is so very important. Breaking compatibility between versions is one thing. Breaking URL output consistency is a next level of bad problem we want to avoid AT ALMOST ANY COST (sorry for all caps but I really don’t want to break any URLs). Changing the defaultslug
to point to a new package would be a not just be a breaking change but would also (in my opinion, at bare minimum) require some extra code to compare old with new URLs to warn about URL changes.There is a special case of the above I ran into the other day on win10 dev environment. Because the the current slug filter does not remove dots from titles, if your title happens to have a one at the end, and you use titles for your URLs you can end up with something like: this-is-my-title./index.html
Having a dot at the end of a directory name or file turns out to be problematic in windows, and requires a rather roundabout way of deleting it: https://stackoverflow.com/questions/4075753/how-to-delete-a-folder-that-name-ended-with-a-dot
Figured some may find this useful if they have cleanup scripts to blow away the _site directory, and those scripts are suddenly failing on windows for unknown reasons, this could be one of them.
Thanks for sharing @kazzkiq! I wonder if you could just use slug? Does it overwrite the built in filter? I feel like that should work if it doesn’t
Purely for comedic purposes, but I think this might possibly work if we have a future [Eleventy v1]
slug
filter (which uses slugify), and aslugify
filter (which uses @sindresorhus/slugify), but you want to alias one to the other (because you like the “slug” filter name, but newer “slugify” implementation…Yeah, it has great potential to break your existing URLs because it’s a completely different implementation, but #YOLO. 💥
https://github.com/11ty/eleventy/pull/1873 is open for feedback! I stuck with the 1.x
@sindresorhus/slugify
package for now and decided to stick with theslugify
name for brevity and to match the upstream package name. We can revisit 2.x when more ESM stuff lands in Eleventy.This is as close as I’ve ever gotten to using the ESM version in commonjs/Node. It seems to work for Liquid (9.1.3+ via eleventy@canary build) and Nunjucks (via
.addNunjucksAsyncFilter()
).Definitely easier to stick with @sindresorhus/slugify v1 (pre-ESM). I tried, but could never figure out how to get Node’s
util.callbackify()
function to work w/ @sindresorhus/slugify, which might have been able to shave a couple lines off of that code.Oh, I wasn’t suggesting 11ty change the default slugify library (although personally I think
@sindresorhus/slugify
is the better option). I think it’d have too many issues w/ backwards compatibility and broken/changed links. But it’s an easy fix if you want to opt-in for individual sites.Excellent, thank you @okitavera.
@kazzkiq I think you should be able to use custom filters in permalink generation - have you tried?
Oh, hmm. Well that’s not ideal.
I did try
permalink: '/{{ "Hi I''m Zach" | slug }}/'
(note the escaped single quote using''
) which resulted in the URLhttp://localhost:8080/hi-i'm-zach/
which worked in both Chrome and Firefox. Where is the conversion happening in your URL? It doesn’t seem to be coming from the slugify filter.