With shadcn-svelte
-
Install Astro and shadcn-svelte following these instructions
-
Install the astro-superforms integration following these instructions
-
Install some components from shadcn-svelte
Terminal window pnpm dlx shadcn-svelte@latest add card form input sonner -
Create a validation schema and a type for your message
src/schemas.ts import { z } from "astro/zod";import type { ActionError } from "astro:actions";export const zNewsletterValues = z.object({email: z.string().email().min(1),});export type NewsletterValues = z.infer<typeof zNewsletterValues>;export type Message = ActionError["code"] | "SUCCESS"; -
Create your action
src/actions/index.ts import { zNewsletterValues } from "@/lib/utils";import { defineAsfAction } from "superforms:astro";import { message } from "sveltekit-superforms";export const server = {//...subscribeToNewsletter: defineAsfAction({input: zNewsletterValues,handler: async ({ email }, { form }) => {await new Promise((resolve) => setTimeout(resolve, 2000));const response = Math.random();if (response < 0.3) return message<Message>(form, "BAD_REQUEST", { status: 400 });if (response < 0.5) return message<Message>(form, "CONFLICT", { status: 409 });return message<Message>(form, "SUCCESS");},}),//...}; -
Initialize the form in your astro index page
src/pages/index.astro ---import "$lib/styles/app.css";import { superValidate } from "sveltekit-superforms";import { zod } from "sveltekit-superforms/adapters";import { zNewsletterValues } from "$lib/schemas";import Form from "./_form.svelte";const sv = await superValidate(Astro.request, zod(zNewsletterValues));---<html lang="en"><head><meta charset="utf-8" /><link rel="icon" type="image/svg+xml" href="/favicon.svg" /><meta name="viewport" content="width=device-width" /><meta name="generator" content={Astro.generator} /><title>Astro</title></head><body><body class="flex items-center justify-center min-h-screen"><Form client:load {sv} /></body></html> -
Display the form in your svelte component
src/pages/_form.svelte <script lang="ts">import * as Card from "$lib/components/ui/card";import * as Form from "$lib/components/ui/form";import { Input } from "$lib/components/ui/input";import { Toaster } from "$lib/components/ui/sonner";import { zNewsletterValues, type Message, type NewsletterValues } from "$lib/schemas";import { actions } from "astro:actions";import { toast } from "svelte-sonner";import { superForm, type SuperValidated } from "sveltekit-superforms";import { zodClient } from "sveltekit-superforms/adapters";let { sv }: { sv: SuperValidated<NewsletterValues, Message> } = $props();const sf = superForm(sv, {validators: zodClient(zNewsletterValues),onError: () => {toast.error(i18n());},onUpdated: ({ form: { message, valid } }) => {if (message) valid ? toast.success(i18n(message)) : toast.error(i18n(message));},});const { delayed, enhance, form, submitting } = sf;function i18n(code?: Message) {if (code === "BAD_REQUEST") return "An error occurred. Please try again later.";if (code === "CONFLICT") return "You are already subscribed.";if (code === "SUCCESS") return "Successfully subscribed.";return "An error occurred. Please try again later.";}</script><Card.Root class="w-full max-w-md"><form method="POST" action={actions.subscribeToNewsletter} use:enhance novalidate><Card.Header><Card.Title>Newsletter</Card.Title><Card.Description>Please enter your email address to subscribe to our newsletter.</Card.Description></Card.Header><Card.Content><Form.Field form={sf} name="email"><Form.Control>{#snippet children({ props })}<Input {...props} type="email" bind:value={$form.email} />{/snippet}</Form.Control><Form.FieldErrors /></Form.Field></Card.Content><Card.Footer><Form.Button disabled={$submitting}>{$delayed ? "Subscribing..." : "Subscribe"}</Form.Button></Card.Footer></form></Card.Root><Toaster />