I’d like to group some of my pages in Kirby using folders (or something similar), but I don’t want the folder slug to appear in the final URLs. I’ve heard about different approaches—like custom routing, virtual pages, or using regular pages with overridden routes—but I’m not sure which method is best or recommended. Does anyone have advice or examples on how to set this up?
Here’s a screenshot of what I’m trying to achieve:
I can also imagine plugin which will add Tags above the pages section so I can view group of pages. Some client have quite a lot pages and it is messy. Pagination and filtering don’t help in this case. And I know that I can show pages with certain templates, but it’s not a solution either.
The problem with the subfolder approach and routing to get rid of the extra slug, is that you have no control over duplicate slugs. Which may become an issue with a lot of pages. So if you have a page-a in folder-a and a page-a in folder-b, one of the pages will not be accessible anymore.
Having said that, the best way to go about this is a combination of routes and ideally page models that modify the url method accordingly.
Maybe virtual parents with virtual children would be better here just for displaying them in groups.
But why not tag them them and display them in different sections?
@texnixe you’re right! I have solved this exact issue with a plugin that:
Uses hashes as folder names (e.g., 30c3c31c/page-a)
Removes the hash from URLs using custom routes (displaying only /page-a)
Most importantly: Prevents duplicate slugs across different folders
The key component is implementing hooks that check for duplicates before creating or modifying pages:
php
'hooks' => [
'page.create:before' => function ($page) {
// Check if a page with the same slug already exists in any hash folder
if ($parent && site()->isHashFolder($parent)) {
$duplicate = site()->findDuplicateInHashFolders($slug);
if ($duplicate) {
throw new Exception('A page with this slug already exists');
}
}
},
// Similar checks for page.update:before and page.changeSlug:before
]
The plugin adds a findDuplicateInHashFolders() method that searches through all hash folders for duplicate slugs.
This approach gives you folder organization without showing folders in URLs, while preventing conflicts that would cause content to become inaccessible or lost.
Hm, looping throw the whole site can be a performance issue with many pages. I’d consider storing (and updating) some log file. And note that you might run into the same issue not only when creating a page, but also when changing the slug of a page.