# 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:

```kiln
{% include "components/button" with { label: "Sign up", href: "/join" } %}
{% include "components/button" with { label: "Cancel", variant: "secondary" } %}
```

components/button.kiln:

```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:

```html
<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.

```kiln
{% 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:

```kiln
{% include "components/button" with { label: "Go", variant: "danger" } %}
```

components/button.kiln:

```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:

```text
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.

```kiln
{% 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](https://kilnlang.com/docs/api/types/#your-own-types).

page.kiln:

```kiln
{% props post: Post %}
<a href="{{ post.url }}">{{ post.title }}</a>
```

Output:

```html
<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`](https://kilnlang.com/docs/functions/#attrs).

page.kiln:

```kiln
{% include "components/button" with { label: "Close", class: "button--small", "aria-label": "Close dialog" } %}
```

components/button.kiln:

```kiln
{% props label: string, ...attributes %}
<button{{ attrs({ ...attributes, class: ["button", attributes.class ?? null] }) }}>{{ label }}</button>
```

Output:

```html
<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](https://kilnlang.com/docs/api/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:

```kiln
{% extends "layouts/base" %}
{% set title = "About us" %}
{% block content %}<h1>{{ title }}</h1>{% endblock %}
```

layouts/base.kiln:

```kiln
{% props title %}
<title>{{ title }}</title>
{% block content %}{% endblock %}
```

Output:

```html
<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](https://kilnlang.com/docs/api/errors/#checking-templates).
