Skip to content

Latest commit

 

History

History
192 lines (140 loc) · 5.86 KB

File metadata and controls

192 lines (140 loc) · 5.86 KB
title Simple form
description Simple Form (simple-stack-form) is a deprecated Astro integration for Zod-based form validation. Use Astro Form Actions instead for server-side form handling.
sidebar
label order
Get started
1

import { Tabs, TabItem } from '@astrojs/starlight/components';

:::caution ⚠️ This package is no longer maintained. Astro now has Server Islands, which largely replace the use cases explored by this package. Server Islands also offer a simpler mental model for streaming content that scales to a number of deployment hosts.

That said, if you want to understand how this package works to fork the functionality for your own use, watch my YouTube walkthrough! :::

The simple way to validate forms in your fullstack app.

---
// src/pages/index.astro

import { z } from "zod";
import { createForm } from "simple:form";

const checkout = createForm({
  quantity: z.number(),
  email: z.string().email(),
  allowAlerts: z.boolean(),
});

const result = await Astro.locals.form.getData(checkout);

if (result?.data) {
  await myDb.insert(result.data);
  // proceed to checkout
}
---

<form method="POST">
  <label for="quantity">Quantity</label>
  <input id="quantity" {...checkout.inputProps.quantity} />

  <label for="email">Email</label>
  <input id="email" {...checkout.inputProps.email} />

  <label for="allowAlerts">Allow alerts</label>
  <input id="allowAlerts" {...checkout.inputProps.allowAlerts} />
</form>

Installation

Simple form is an Astro integration. You can install and configure this via the Astro CLI using astro add:

npm run astro add simple-stack-form

After installing, you'll need to add a type definition to your environment for editor hints. Add this reference to a new or existing src/env.d.ts file:

// env.d.ts
/// <reference types="simple-stack-form/types" />

Simple form can be used with any framework. You can install it via npm:

# npm
npm i simple-stack-form

# pnpm
pnpm i simple-stack-form

Create a validated form

Type: createForm(ZodRawShape): { inputProps: Record<string, InputProps>, validator: ZodRawShape }

You can create a simple form with the createForm() function. This lets you specify a validation schema using Zod, where each input corresponds to an object key. Simple form supports string, number, or boolean (checkbox) fields.

import { createForm } from "simple:form";
import z from "zod";

const signupForm = createForm({
  name: z.string(),
  age: z.number().min(18).optional(),
  newsletterOptIn: z.boolean(),
});

createForm() returns both a validator and the inputProps object. inputProps converts each key of your validator to matching HTML props / attributes. The following props are generated today:

  • name - the object key.
  • type - checkbox for booleans, number for numbers, and text for strings.
  • aria-required - true by default, false when .optional() is used. Note aria-required is used to add semantic meaning for screenreaders, but leave room to add a custom error banner.

Our signupForm example generates the following inputProps object:

const signupForm = createForm({
  name: z.string(),
  age: z.number().min(18).optional(),
  newsletterOptIn: z.boolean(),
});

signupForm.inputProps;
/*
  name: { name: 'name', type: 'text', 'aria-required': true }
  age: { name: 'age', type: 'number', 'aria-required': false }
  newsletterOptIn: { name: 'newsletterOptIn', type: 'checkbox', 'aria-required': true }
*/

Handle array values

You may want to submit multiple form values under the same name. This is common for multi-select file inputs, or generated inputs like "add a second contact."

You can aggregate values under the same name using z.array() in your validator:

import { createForm } from "simple:form";
import z from "zod";

const contact = createForm({
  contactNames: z.array(z.string()),
});

Now, all inputs with the name contactNames will be aggregated. This uses FormData.getAll() behind the scenes:

---
import { createForm } from "simple:form";
import z from "zod";

const contact = createForm({
  contactNames: z.array(z.string()),
});

const res = await Astro.locals.form.getData(contact);
console.log(res?.data);
// contactNames: ["Ben", "George"]
---

<form method="POST">
  <label for="contact-1">Contact 1</label>
  <input id="contact-1" {...contact.inputProps.contactNames} />
  {res.fieldErrors?.contactNames?.[0]}
  <label for="contact-2">Contact 2</label>
  <input id="contact-2" {...contact.inputProps.contactNames} />
  {res.fieldErrors?.contactNames?.[1]}
</form>

Note that fieldErrors can be retrieved by index. For example, to get parse errors for the second input, use fieldErrors.contactNames[1].

Sanitize User Input

You may need to sanitize user input with rich text content. This is important for any text rendered as HTML to prevent Cross-Site Scripting (XSS) attacks. You can use the sanitize-html library for this:

npm install --save sanitize-html
npm install --save-dev @types/sanitize-html

Next, call sanitize-html from your text validator with a Zod transform():

+ import sanitizeHtml from "sanitize-html";

const signupForm = createForm({
-  name: z.string(),
+  name: z.string().transform((dirty) => sanitizeHtml(dirty)),
  age: z.number().min(18).optional(),
  newsletterOptIn: z.boolean(),
});

You can find a sanitization example in our Astro playground