Form Actions
Create, update, and delete records with declarative HTML attributes. No JavaScript, no API routes — just forms.
Create
Add w-action="create" and w-store="collection" to a form. Each <input> name becomes a field in the new record.
<form w-action="create" w-store="posts" w-redirect="/blog">
<input name="title" type="text" placeholder="Post title">
<textarea name="content" placeholder="Write something..."></textarea>
<button type="submit">Publish</button>
<button type="reset">Cancel</button>
</form>
On submit, the framework creates a new record in the "posts" collection with an auto-generated id and created_at timestamp, then redirects to /blog.
Update
Use w-action="update" with w-id to specify which record to modify. Only the submitted fields are updated.
<form w-action="update" w-store="posts" w-id="#post.id#" w-redirect="/blog">
<input name="title" type="text" value="#post.title#">
<textarea name="content">#post.content#</textarea>
<button type="submit">Save Changes</button>
<button type="reset">Cancel</button>
</form>
Delete
Use w-action="delete" with w-id. No input fields are needed — just a submit button.
<form w-action="delete" w-store="posts" w-id="#post.id#" w-redirect="/blog">
<button type="submit">Delete Post</button>
</form>
Redirects
The w-redirect attribute sets where the user goes after a successful action.
<!-- Redirect to a specific page -->
<form w-action="create" w-store="posts" w-redirect="/blog">
<!-- Redirect to a dynamic URL -->
<form w-action="update" w-store="posts" w-id="#post.id#" w-redirect="/blog/#post.id#">
If w-redirect is not specified, the form redirects back to the referring page.
CSRF Protection
Every form is automatically protected against cross-site request forgery. The engine injects a hidden _csrf field and a <meta name="csrf-token"> tag. No manual work is needed.
<!-- You write this: -->
<form w-action="create" w-store="posts">
<input name="title">
<button type="submit">Create</button>
</form>
<!-- The engine renders this: -->
<form action="/w-action/posts?w-action=create" method="post">
<input type="hidden" name="_csrf" value="...">
<input name="title">
<button type="submit">Create</button>
</form>
Flash Messages
After a form action, the framework sets flash messages in the session. Display them with #flash.success# and #flash.error#.
<if flash.success>
<div class="alert alert-success">#flash.success#</div>
</if>
<if flash.error>
<div class="alert alert-danger">#flash.error#</div>
</if>
Flash messages are consumed on read — they appear once and are cleared from the session.
Partial Updates
Instead of a full page redirect, update a specific part of the page using w-target and w-swap.
<form w-action="create" w-store="comments" w-target="#comments-list" w-swap="append">
<input name="body" placeholder="Add a comment...">
<button type="submit">Post</button>
</form>
<div id="comments-list">
<loop data="#comments#" as="comment">
<p>#comment.body#</p>
</loop>
</div>
Available swap modes:
| Value | Behavior |
|---|---|
innerHTML | Replace the target's inner content (default) |
append | Insert at the end of the target (alias: beforeend) |
prepend | Insert at the start of the target (alias: afterbegin) |
before | Insert before the target element (alias: beforebegin) |
after | Insert after the target element (alias: afterend) |
outerHTML | Replace the target element entirely |
none | Don't swap, just trigger the action |
Confirmation Dialogs
Add w-confirm to show a browser confirmation dialog before submitting. If the user cancels, the form is not submitted.
<form w-action="delete" w-store="posts" w-id="#post.id#" w-redirect="/blog">
<button type="submit" w-confirm="Are you sure you want to delete this post?">
Delete
</button>
</form>
Complete CRUD Example
A full page with a create form, a list of items, and edit/delete actions:
<what>
fetch.tasks = "local:tasks"
fetch.tasks.sort = "created_at:desc"
</what>
<h1>Tasks</h1>
<if flash.success>
<div class="alert alert-success mb-4">#flash.success#</div>
</if>
<!-- Create form -->
<form w-action="create" w-store="tasks" w-redirect="/tasks" class="flex gap-2 mb-6">
<input name="title" type="text" placeholder="New task..." class="input flex-1">
<input name="status" type="hidden" value="pending">
<button type="submit" class="btn btn-primary">Add Task</button>
</form>
<!-- Task list -->
<if tasks>
<loop data="#tasks#" as="task">
<div class="flex items-center justify-between p-3 border-b">
<div>
<strong>#task.title#</strong>
<span class="badge ml-2">#task.status#</span>
</div>
<div class="flex gap-2">
<form w-action="update" w-store="tasks" w-id="#task.id#" w-redirect="/tasks">
<input name="status" type="hidden" value="done">
<button type="submit" class="btn btn-sm">Complete</button>
</form>
<form w-action="delete" w-store="tasks" w-id="#task.id#" w-redirect="/tasks">
<button type="submit" class="btn btn-sm btn-danger"
w-confirm="Delete this task?">Delete</button>
</form>
</div>
</div>
</loop>
</if>
<else/>
<p class="text-muted p-4">No tasks yet. Add one above.</p>
</else>