Manage a task list
Adding and removing repeated items.
View MarkdownComplete the Forms setup first, or use the configuration, middleware, and layout files in the sidebar. Install @astro-utils/forms and @astrojs/node in an existing Astro project.
Try the interaction
Section titled “Try the interaction”Add a task, then remove it. Removal does not require a valid value in the new-task field.
src/layouts/Layout.astro
---import { WebForms } from '@astro-utils/forms/forms.js';---<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width" /> <title><slot name="title" /></title> </head> <body> <WebForms> <slot /> </WebForms> </body></html>src/pages/todo.astro
---import { BButton, BInput, Bind, BindForm } from '@astro-utils/forms/forms.js';
import Layout from '../layouts/Layout.astro';
type TaskForm = { newTask: string; message: string;};
type TodoTask = { title: string; id: string;}
const bind = Bind<TaskForm>({ newTask: '', message: '' });const tasks: TodoTask[] = Astro.locals.session.tasks ??= [{ id: 'limits', title: 'Review upload limits' }, { id: 'notes', title: 'Publish migration notes' }];
async function addTask() { tasks.push({title: bind.newTask, id: crypto.randomUUID()});
bind.message = `${bind.newTask} added.`; bind.newTask = '';}
async function removeTask(id: string, title: string) { const index = tasks.findIndex(task => task.id === id); if (index === -1) { bind.message = 'This task has already been removed.'; return; } tasks.splice(index, 1);
bind.message = `${title} removed.`;}---
<Layout><span slot="title">Manage a task list</span><BindForm {bind} omitState={['message']}> <!-- omitState = ensure this info will be removed in the next action --> {bind.message && <p role="status">{bind.message}</p>}
<label for="newTask">New task</label> <BInput id="newTask" name="newTask" minlength={2} maxlength={80} required /> <BButton onClick={addTask} whenFormOK>Add task</BButton>
<ul> {tasks.map(task => ( <li> <span>{task.title}</span> <BButton onClick={() => removeTask(task.id, task.title)}>Remove</BButton> </li> ))} </ul></BindForm></Layout>src/middleware.ts
import forms from '@astro-utils/forms';
export const onRequest = forms();astro.config.mjs
import { defineConfig } from 'astro/config';import node from '@astrojs/node';import forms from '@astro-utils/forms/dist/integration.js';
export default defineConfig({ output: 'server', adapter: node({ mode: 'standalone' }), integrations: [forms]});Preview Interactive demo
How it works
Section titled “How it works”The runnable source stores a small list in the signed session cookie. It survives page reloads while that session remains valid; it is not shared between devices. Keep the list small or move it to application storage. The removal handler checks that the item still exists before changing the list.
Continue
Section titled “Continue”Learn about stable keys and reusable components.