Forms & CSRF
HTMX posts forms over AJAX and swaps the response. StreetJS handles validation and CSRF the same way it does for any request — you just return HTML instead of JSON.
CSRF
Add a hidden CSRF field to forms with the helper (token comes from the StreetJS session/CSRF layer):
1
2
3
import { csrfField } from '@streetjs/plugin-htmx';
// in a page's data:
ctx.htmx.view('login', { title: 'Log in', csrf: csrfField(ctx.csrfToken) });
1
2
3
4
5
6
7
<form hx-post="/login" hx-target="#error">
}
<div id="error"></div>
<input name="email" type="email" required>
<input name="password" type="password" required>
<button>Log in</button>
</form>
The field name defaults to _csrf; pass a second argument to match your CSRF
middleware: csrfField(token, 'csrf_token').
Validation errors as fragments
Return an error fragment targeted at #error (HTMX swaps it in) and a non-200
status — HTMX still swaps 4xx bodies when configured, or use hx:
1
2
3
4
5
6
7
8
9
10
11
@Post('/login')
async login(ctx: StreetContext) {
const { email, password } = ctx.body as { email: string; password: string };
const user = await this.auth.verify(email, password);
if (!user) {
ctx.htmx.fragment('<p class="error">Invalid credentials.</p>', 401);
return;
}
// success → redirect the client (see Authentication)
ctx.htmx.hx({ redirect: '/dashboard' }).fragment('');
}
Always escape user input
`` escapes by default. Only use } for HTML you trust (like the CSRF
field or pre-rendered partials).
Next: Authentication.