Tags
props
Declare the variables a template expects.
props declares the variables a template expects, with optional types and defaults. It is most useful in components and included templates, where it documents and checks their inputs.
{% include "components/button" with { label: "Sign up", href: "/join" } %}
{% include "components/button" with { label: "Cancel", variant: "secondary" } %}
{% props label: string, href: string | null = null, variant: "primary" | "secondary" = "primary" %}
{% if href %}
<a class="button button--{{ variant }}" href="{{ href }}">{{ label }}</a>
{% else %}
<button class="button button--{{ variant }}">{{ label }}</button>
{% endif %}
<a class="button button--primary" href="/join">Sign up</a>
<button class="button button--secondary">Cancel</button>
Rules
propsmust come before any other output, and a template can have only one.- A prop without a default is required.
- A default is used when the caller passes nothing or null.
- In strict mode, passing a variable the template doesn't declare is an error. This catches typos in the caller.
- Templates without
propsaccept any variables.
Types
Add a type after a colon. Join several with |. Kiln checks the value after the default applies.
{% props
title: string,
href: string | null = null,
level: 2 | 3 = 2,
variant: "primary" | "secondary" = "primary",
tags: array = []
%}
The types are null, boolean, number, string, array, hash, set, date, markup and object. A string or number, such as "primary", accepts only that exact value.
- A prop that may be null must include
nullin its type. stringalso accepts safe markup.- Untyped and typed props can be mixed.
{% include "components/button" with { label: "Go", variant: "danger" } %}
{% props label: string, href: string | null = null, variant: "primary" | "secondary" = "primary" %}
{% if href %}
<a class="button button--{{ variant }}" href="{{ href }}">{{ label }}</a>
{% else %}
<button class="button button--{{ variant }}">{{ label }}</button>
{% endif %}
Prop "variant" of components/button.kiln must be "primary" | "secondary", but got "danger" in components/button.kiln at line 1, column 1.
Lists and shapes
Describe what a list holds with [], and the keys a hash has with braces. Mark an optional key with ?. Use brackets to group a union.
{% props
tags: string[],
levels: (2 | 3)[] = [],
author: { name: string, url?: string | null }
%}
string[]accepts an array.- A shape accepts a hash or an object, such as a class instance.
- Shapes and lists can be nested, such as
{ links: { href: string }[] }.
Your own types
A type name that starts with a capital letter, such as Post, is one your app defines. Register a check for it when you create the environment. See TypeScript types.
{% props post: Post %}
<a href="{{ post.url }}">{{ post.title }}</a>
<a href="/blog/hello-world/">Hello, world</a>
Using a type that isn't registered is an error.
Other variables
End props with ... and a name to collect every variable the caller passes that isn't declared. They arrive as a hash, so a component can pass attributes such as class and aria-label on to its markup with attrs.
{% include "components/button" with { label: "Close", class: "button--small", "aria-label": "Close dialog" } %}
{% props label: string, ...attributes %}
<button{{ attrs({ ...attributes, class: ["button", attributes.class ?? null] }) }}>{{ label }}</button>
<button class="button button--small" aria-label="Close dialog">Close</button>
- The rest prop comes last, and has no type or default.
- It is an empty hash when the caller passes nothing extra.
- In strict mode, a template with a rest prop accepts any variable.
- In a layout reached through
extends, it is always empty, because the variables were meant for the page.
How values are checked
Kiln checks types in strict mode, after defaults apply. It checks the outer value only, so it stays fast however much data a template gets:
tags: string[]checks thattagsis an array, not that every item is a string.- A shape checks that the value is a hash or an object, not each key.
- Your own types run the check you registered.
Strict mode still catches a missing key where the template reads it. For a full check of nested data before you deploy, generate TypeScript types from your templates.
In layouts
A layout reached through extends can use props too. It checks required props and applies defaults, but doesn't reject other variables, because the render data was meant for the page.
A page can provide a layout's props with a top-level set, as well as through the data you render it with.
{% extends "layouts/base" %}
{% set title = "About us" %}
{% block content %}<h1>{{ title }}</h1>{% endblock %}
{% props title %}
<title>{{ title }}</title>
{% block content %}{% endblock %}
<title>About us</title>
<h1>About us</h1>
Checking without rendering
check compares each include, component and extends with the props of the template it uses, so a misspelt or missing prop shows up before the page renders. A page with a rest prop can be given any prop its layout needs, so those aren't reported. See Errors and checks.