Project layout, routing and assets

How files become URLs, plus partials, error pages, assets and Sass.

A Crisptastic site is a folder. The structure of the folder decides the URLs. There is no router file.

The smallest possible site

One file is enough:

my-site/
  index.html.crisp
<!doctype html>
<h1>Hello, {{= request.path }}</h1>

Run crisptastic dev inside the folder and open http://127.0.0.1:6174/. No crisptastic.toml is needed. crisptastic.toml becomes necessary when you want to configure something, and is required to deploy.

A typical site

my-site/
  crisptastic.toml                 configuration (optional while developing)
  index.html.crisp                 /
  about.html.crisp                 /about
  blog/
    index.html.crisp               /blog
    [slug].html.crisp              /blog/:slug
  sitemap.xml.crisp                /sitemap.xml
  404.html.crisp                   error page for 404 (top level only)
  _partials/
    _layout-open.html.crisp        partial: never served
    _layout-close.html.crisp       partial: never served
  assets/
    style.scss                     served as /style.css
    logo.svg                       served as /logo.svg

How a file becomes a route

The router looks at every file under the site root whose name ends in .crisp, including in sub-folders. It does not skip hidden folders or node_modules, so keep unrelated .crisp files out of the tree.

Source fileURL
index.html.crisp/
about.html.crisp/about
contact/index.html.crisp/contact
notes.md.crisp/notes
api/data.json.crisp/api/data.json
sitemap.xml.crisp/sitemap.xml
robots.txt.crisp/robots.txt
a.b.html.crisp/a.b
blog/[slug].html.crisp/blog/:slug
users/[id]/posts/[post].html.crisp/users/:id/posts/:post

For .html and .md pages the extension is dropped from the URL. For other formats (.json, .xml, .txt, .css, ...) it is kept. The supported formats are html md txt xml json css js mjs svg csv; see Pages and templating.

Trailing slashes do not matter: /about, /about/ and //about all reach /about, and no redirect is issued. Matching is case-sensitive. Because no redirect is issued, a public page exists under both spellings; compare against request.route.path (always /about), not request.path (which keeps the slash as requested), and point canonical links at the slash-less form.

Dynamic segments

A folder or file named [name] matches exactly one non-empty path segment. The value is available as request.route.params.name.

blog/[slug].html.crisp
<h1>{{ request.route.params.slug }}</h1>
  • name may contain only letters, digits and _.
  • [slug] must be the whole segment. [id].json.crisp is an error, so use items/[id]/index.json.crisp for /items/:id.
  • There are no catch-all ([...rest]) or optional segments.
  • Static segments beat dynamic ones: with both blog/new.html.crisp and blog/[slug].html.crisp, the path /blog/new goes to the static page.
  • Two routes that would match the same URLs, such as about.html.crisp plus about/index.html.crisp, or [a]/x plus [b]/x, fail the build with a duplicate route or ambiguous route error.

Methods

A route with a [routes] declaration accepts the methods it names. Without one, it answers GET, HEAD and POST, and PUT, PATCH and DELETE get 405. A page decides what to do with a POST by looking at request.method; see Sessions and forms.

Partials: files that are never served

A file is private if its file name starts with _. The folder name is not considered.

PathResult
_partials/_header.html.crispprivate, includable
_partials/header.html.crisppublic at /_partials/header
_header.html.crispprivate
_errors/404.html.crisppublic at /_errors/404, and not an error page

The _partials/ folder is a convention only; it is the leading _ on the file that keeps it private. Partials are still compiled, so they must be valid even though nothing serves them.

Include them from a page with @include("_partials/_header.html.crisp"). The path is relative to the including file.

Error pages

Error pages are files at the top level of the site with the exact names 400, 404, 500 or error (with .html.crisp or .md.crisp). See Sessions and forms for the lookup rules.

Static assets and Sass

Files in the assets/ folder (or the folder named by assets in crisptastic.toml or --assets) are served as static files.

  • They are served from the site root, not from /assets/. assets/logo.svg is /logo.svg; assets/img/a.png is /img/a.png.
  • A route beats an asset at the same URL if the route is static. An asset beats a route that has [param] segments.
  • A request for /site.css is answered by compiling assets/site.scss (or .sass) if there is no real site.css. Files whose name starts with _ are Sass partials and are never emitted as CSS.
  • Sass is compiled by an embedded compiler. Extra search folders can be given with sass_load_paths or --sass-load-path.
  • All Sass is compiled at startup, so a Sass error stops dev from starting. After startup, a Sass error keeps serving the last good CSS and prints a warning.
  • Content types come from the file extension: css js mjs json webmanifest xml csv map svg png jpg jpeg gif webp avif ico woff woff2 ttf otf pdf mp3 mp4 webm wasm html txt<code>. Everything else is</code>application/octet-stream.
  • Nothing sets caching headers on assets.
  • Raw .scss sources are downloadable (for example /site.scss), as are any other files in the assets folder. Do not put anything private there.

In production, assets are not served from the host at all by default. See Deploying.

Includes between formats

An HTML page can include a Markdown partial, which is the usual way to write long prose:

@include("_partials/_layout-open.html.crisp")
@include("_partials/_about-body.md.crisp")
@include("_partials/_layout-close.html.crisp")

Details and restrictions are in Pages and templating.

Checking a site without running it

crisptastic check ./my-site     # compile; prints "ok: 5 routes, 3 partials"
crisptastic routes ./my-site    # list every route pattern

routes prints dynamic segments as :name. Error pages and partials are not listed. If a route you expected is missing from the output, it is probably a partial (file name starts with _).