# Headless and HATEOAS Source: https://kart.bnomei.com/docs/forms/headless-and-hateoas Updated: 2025-08-08T14:06:44+00:00 Summary: Guide to using Kirby Kart plugin: integrate HTML forms or headless apps with JSON/CSRF, and leverage HATEOAS via Htmx for dynamic cart and wishlist interactions ## HTML Forms By default, you would use HTML forms like this to send data to the Kirby Kart plugin routes. Code (php): ``` inStock()) { ?>
Out of stock
{ const [inCart, setInCart] = createSignal(props.product.inCart); // Initial state const toggleCart = async () => { const url = inCart() ? `/kart/cart-remove?product=${props.product.uuid.id}` : `/kart/cart-add?product=${props.product.uuid.id}`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": csrfToken(), }, }); if (response.ok) { setInCart(!inCart()); // Toggle state on success } }; return props.product.inStock ? ( ) : (Out of stock
); }; export default CartToggleButton; ``` > TIP: You can use the `kart/csrf`-endpoint to retrieve the current CSRF session token. > TIP: The Kirby Kart plugin supports KQL in exposing the most common props by default. > TIP: Instead of relying on the default behaviour of the router you can also use snippets to customize the JSON output. See HATEOAS example below for more details. ## HATEOAS If you want your website to swap HTML instead of making a form request and reloading then [Htmx](https://htmx.org) or [Datastar](https://data-star.dev) are great choices. The Kirby Kart router will automatically detect the `HX-Request`-header sent by Htmx and respond with a matching snippet (see below). While you could force the Kirby Kart router to always respond with HTML in setting the `bnomei.kart.router.mode = html` in the config you might not want that for every request. It might be easier to send a fake `HX-Request` header for Datastar instead. The `bnomei.kart.router.snippets` config setting provides a default mapping for snippets. Only the snippets listed in the array will be called and you need to overwrite this setting to add your own. Code (php): ``` 'bnomei.kart.router.snippets' => [ // define the snippets that are allowed to be called 'kart/cart-add', 'kart/cart-buy', 'kart/cart-later', // dummy 'kart/cart-remove', // dummy 'kart/captcha', 'kart/login', 'kart/login-magic', 'kart/signup-magic', 'kart/wish-or-forget', 'kart/wishlist-add' => 'kart/wish-or-forget.htmx', // htmx 'kart/wishlist-now', // dummy 'kart/wishlist-remove' => 'kart/wish-or-forget.htmx', // htmx // overwrite to change or set your own ], ``` For all plain values, the router will respond with the HTML code generated by the snippet. So the route `kart/cart-add` will render the snippet `site/snippets/kart/card-add.php`. Note the two mappings that have the Htmx comment. Here the route is mapped to a snippet with a different name to allow the dynamic behaviour. `kart/wishlist-add` outputs `site/snippets/kart/wish-or-forget.htmx.php` and the route `kart/wishlist-route` outputs `site/snippets/kart/wish-or-forget.htmx.php` as well. This works since the snippet is aware of the state of the product in the wishlist. Path: site/snippets/kart/wish-or-forget.htmx.php Code (php): ``` wishlist()->has($product)) { ?>