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.