I’m trying to generate URLs for alternative template types (like .md instead of the default HTML) while preserving query parameters, and I’m wondering if there’s a cleaner way than manually constructing the URL.
Is there a built-in Kirby method to get a page URL with a different template type (like .md) that properly handles query parameters? I don’t see this documented anywhere — is there a recommended approach for linking to alternative template types?
Use case
I have a posts.md.php template that outputs markdown, and I want to link to it from the regular HTML posts.twig template while preserving any tag filters that might be active.
(As a sidenote, when query parameters are present, the resulting URL format is http://localhost:8000/posts.md/tag:Hello — which is a bit awkward format, in my opinion — not a blocker, just noting it.)
Any suggestions or workarounds would be appreciated!
I’m not aware of a method that returns the url for a content representation, but you could create a method in a page model (e.g. $page->represenationUrl()), which also allows you to pass parameters.
If you want to use query format (i.e. https://mydomain.tld?tag=hello) instead of the params, you can use the query option instead of the paramsoption:
Thanks for the suggestion! I’ve actually created a custom urlExtended() method as a page extension that handles this use case. Here’s what I’ve implemented:
What it does:
Adds support for a type parameter to generate URLs with content type extensions (e.g., .md)
Supports both params (path-based) and query (query string) simultaneously
Normalizes query and params to be interchangeable (handles Kirby Params/Query objects automatically)
Example usage:
// Regular page with type extension and query params
$page->urlExtended(['type' => 'md', 'query' => kirby()->request()->params()]);
// Produces: "http://localhost:8000/snippets.md?tag=CSS"
// With both params and query (both are used)
$page->urlExtended([
'type' => 'md',
'params' => kirby()->request()->params(),
'query' => kirby()->request()->params()
]);
// Produces: "http://localhost:8000/snippets.md/tag:CSS?tag=CSS"
// Homepage example
site()->homePage()->urlExtended([
'type' => 'md',
'params' => kirby()->request()->params(),
'query' => kirby()->request()->params()
]);
// Produces: "http://localhost:8000/home.md/tag:CSS?tag=CSS"
Why this would be nice to have in core:
Content type representation support: Kirby already supports different content types (.md.php, .json.php, etc.), but there’s no built-in way to dynamically construct URLs to the same page with a different content type. Having this in core would make it much easier to provide alternative representations of content (markdown exports, JSON APIs, etc.).
Interchangeable query/params: Currently, when using query with Kirby’s Params object, you need to call .toArray() first:
It would be a nice quality-of-life improvement if url() could automatically handle Params/Query objects for the query option, similar to how it handles them for params.
The implementation uses Kirby’s Params and Query classes internally, so it follows the same patterns and formatting as core Kirby functionality. It would be great to see this kind of functionality built into url() directly!
Hm, I don’t quite understand why I would want both the param and the query syntax here for the same value? Or, if I leave out the params prop, why I would want to convert params to query syntax?
Totally fair point — having the same value twice as both params and query is not something you’d normally want in a real URL. That example was intentionally a bit silly just to show that the inputs can be treated interchangeably by the helper.
In practice, the “params ↔︎ query” part is mainly useful as a conversion/compat step in edge cases, e.g.:
you receive Kirby-style params (/tag:CSS) but need to redirect to a query-based URL (?tag=CSS) because the target endpoint (or external service) doesn’t understand params
Kirby pagination defaults to params in some setups, but you may prefer query strings for consistency/SEO/tooling
you want to keep existing param-style links working but canonicalize/redirect to query-based ones
That said, this is a secondary feature in the thread — the main thing I’m after is the content-type aware URL generation.
Kirby can already render alternate representations for the same page via content representations (e.g. page.md.php, page.json.php etc.), and Page::url() already supports language switching — but there’s no equivalent way to say “give me the URL to this page in representation X”.
So the core request is basically something like:
$page->url(['type' => 'md'])
// or
$page->url(['representation' => 'md'])
…and it would produce the correct URL with the extension (and then still allow the usual params, query, fragment handling).
My helper just demonstrates that it’s possible and (I hope) ergonomic. The params/query normalization is just a nice-to-have to make it easier to pass Params/Query objects around without thinking too much.
But again — the “why” here is mostly: I want to generate URLs for alternate representations in a clean, official way, similar to how language URLs work today.