/features, /about or a pricing explainer.
What you need
1. Declare the class
static/composer.json
2. Write the entry class
static/src/Theme.php
boot() runs only while your theme is the published one.
3. Add a request handler
static/src/FeaturesView.php
4. Add the template
static/templates/features.twig
5. Install and clear the cache
CACHE=true. During development set CACHE=false; in production clear the cache from Status → Clear cache after publishing a theme that adds routes.
Passing data to the template
Rules
- Keep the theme’s pages public. A page that needs a signed-in user belongs in a plugin under
/app, not a theme. - Handle the landing-page switch. If the public site is disabled, redirect rather than rendering.
- Don’t do heavy work. Marketing pages should hit the database rarely, and never call a third-party API on render.
- Namespace your classes under your own vendor, so two themes can be installed at once without colliding.
Troubleshooting
The route returns 404
The route returns 404
The theme isn’t published, the cache is stale, or
addPath() doesn’t cover the handler’s directory.Class not found
Class not found
Run
composer require acme/aurora, and check that the manifest’s namespace, the PSR-4 map and the class all agree.The page renders without the layout or globals
The page renders without the layout or globals
#[Middleware(ViewMiddleware::class)] is missing, so the ViewResponse was never rendered.