Routing
Basil has two ways to map URLs to handlers: site mode (the filesystem is the router) and explicit routes (listed in basil.yaml). A project can use either or both.
Site Mode
Point site.path at a directory and files in it become routes:
site:
path: ./site
site/
├── index.pars → /
├── about/
│ └── about.pars → /about
└── blog/
└── index.pars → /blog, /blog/anything…
How a request finds its handler
For a request to /blog/2024/hello, Basil walks back from the deepest path segment looking for a handler:
site/blog/2024/hello/hello.pars, thensite/blog/2024/hello/index.parssite/blog/2024/2024.pars, thensite/blog/2024/index.parssite/blog/blog.pars, thensite/blog/index.pars✓- …up to
site/index.pars
Two conventions, checked in order:
- Folder-named file —
blog/blog.parsserves/blog. Easier to tell apart in your editor when you have many folders open. index.pars— the classic.blog/index.parsworks the same way.
Subpaths
The portion of the URL the handler didn't consume is its subpath — so site/blog/index.pars handles /blog/2024/hello with subpath /2024/hello. One handler can render a whole section, React-router-style, by dispatching on it.
Query & form parameters
@params merges URL query parameters and POST form data (form wins on conflicts):
// /search?q=basil
@params.q // "basil"
Explicit Routes
List routes in basil.yaml when you want control over paths, per-route auth, or caching:
routes:
- path: /
handler: ./handlers/index.pars
- path: /dashboard
handler: ./handlers/dashboard.pars
auth: required # See Authentication
- path: /api/*
handler: ./handlers/api.pars
Static Files
Two ways to serve them:
public_dir (the usual way) — files under it are served at the web root:
public_dir: ./public
A file is served at its path inside the directory — the public/ prefix is
not part of the URL. So public/images/logo.png is served at
/images/logo.png, and that URL is how you reference it:
<img src="/images/logo.png" alt="The logo"/>
Start the URL with /: an absolute path works from every page, however deep
the page sits. When a URL matches both a file and a handler, the file wins —
static files are checked first. To compute the URL from the file's path
instead of writing it, use asset():
asset(@./public/images/logo.png) returns "/images/logo.png".
The same rule applies inside CSS. A stylesheet is concatenated into the
/__site.css bundle as-is, so a relative url(…) would
resolve against the bundle's URL, not against where the file sits on disk.
Reference images by their absolute URL:
.hero { background-image: url(/images/logo.png); }
static routes — mount any directory at any prefix:
static:
- path: /static/
root: ./public
There is a third option for a single file: publicUrl() publishes a private
file sitting beside your handler code at a content-hashed URL, without moving
it into public/ — useful when one file should be public but the folder it
lives in should not. See Server Functions on
the Server Globals page.
Asset Bundling
Basil automatically bundles every .css and .js file from your handlers directory into /__site.css and /__site.js (concatenated depth-first alphabetically, cache-busted). Include them with the built-in tags:
<head>
<Css/>
<Script/>
</head>
Both tags render nothing at all when there is no bundle — no files of that type, or no bundler in this context — so they are safe to leave in a layout before you have written any CSS.
<CSS/> and <Javascript/> work as aliases; <Css/> and <Script/> are the
names to use.
See Also
- Configuration —
site,routes,static,public_dir - Authentication — protecting routes
- Parts — routes that return interactive fragments