Skip to content

Search tags, filters, functions and guides.

Tags / props

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.

page.kiln
{% include "components/button" with { label: "Sign up", href: "/join" } %}
{% include "components/button" with { label: "Cancel", variant: "secondary" } %}
components/button.kiln
{% 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 %}
Output
<a class="button button--primary" href="/join">Sign up</a>
<button class="button button--secondary">Cancel</button>

Rules

  • props must 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 props accept 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 null in its type.
  • string also accepts safe markup.
  • Untyped and typed props can be mixed.
page.kiln
{% include "components/button" with { label: "Go", variant: "danger" } %}
components/button.kiln
{% 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 %}
Error
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.

page.kiln
{% props post: Post %}
<a href="{{ post.url }}">{{ post.title }}</a>
Output
<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.

page.kiln
{% include "components/button" with { label: "Close", class: "button--small", "aria-label": "Close dialog" } %}
components/button.kiln
{% props label: string, ...attributes %}
<button{{ attrs({ ...attributes, class: ["button", attributes.class ?? null] }) }}>{{ label }}</button>
Output
<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 that tags is 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.

page.kiln
{% extends "layouts/base" %}
{% set title = "About us" %}
{% block content %}<h1>{{ title }}</h1>{% endblock %}
layouts/base.kiln
{% props title %}
<title>{{ title }}</title>
{% block content %}{% endblock %}
Output
<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.