Skip to content
Kona

Forms

Crema ships a small set of form helpers that read values from the view data map. Pair them with CSRF middleware and validation on the request side.

<form method="post" action="/posts">
@csrf
<input name="title" value="{{ title }}">
<button type="submit">Save</button>
</form>

@csrf renders a hidden _token input. Pass the token as _token or csrf_token in the data map (session middleware typically exposes it):

crema.render(
"posts.create",
mapOf(
"_token" to csrfToken,
"title" to "",
),
)

Output shape:

<input type="hidden" name="_token" value="…">

If neither key is present, the value attribute is empty — the form will fail CSRF checks until you pass a real token.

HTML forms in browsers only submit as GET or POST. Crema’s @method directive emits a hidden field so templates can declare the intended verb:

<form method="post" action="/posts/{{ post.id }}">
@csrf
@method('PUT')
…
</form>

Emits:

<input type="hidden" name="_method" value="PUT">

Common values: PUT, PATCH, DELETE.

<label>
Email
<input name="email" value="{{ email }}">
</label>
@error('email')
<p class="error">{{ errors.email }}</p>
@enderror

The body renders when the view data contains an errors map with a non-empty value for that field:

errors['email'] value @error('email') body
Missing / null Skipped
Empty string or empty collection Skipped
Non-empty string / collection Rendered
Any other non-null value Rendered
crema.render(
"auth.login",
mapOf(
"_token" to token,
"email" to submittedEmail,
"errors" to mapOf("email" to "Required"),
),
)

When validation returns multiple messages per field, pass a list and print them in a loop:

@error('email')
<ul>
@foreach(errors.email as message)
<li>{{ message }}</li>
@endforeach
</ul>
@enderror
@extends('layouts.app')
@section('content')
<h1>Edit post</h1>
<form method="post" action="/posts/{{ post.id }}">
@csrf
@method('PUT')
<label>
Title
<input name="title" value="{{ post.title }}">
</label>
@error('title')
<p class="error">{{ errors.title }}</p>
@enderror
<label>
Body
<textarea name="body">{{ post.body }}</textarea>
</label>
@error('body')
<p class="error">{{ errors.body }}</p>
@enderror
<button type="submit">Update</button>
</form>
@endsection
<link rel="stylesheet" href="@asset('css/app.css')" />

Resolves to /css/app.css by default, or prefixes asset_url / assetUrl from the view data when set (CDN base). See Rendering.

Labels and messages often use @lang — see Rendering.

Why does my @method('PUT') form 405?
The request is still POST. Register a POST route or send a real PUT — see Method spoofing.

Why is the CSRF field empty?
Pass _token or csrf_token in the view data (session middleware usually supplies it).

How do I show old input after validation redirect?
Use flashed old input from the session (request.old("email")) when rendering the form again — Validation.