Use Zod to Validate Forms on the Server with SvelteKit

  • accessible forms
  • data transformation
  • error handling
  • form validation
  • frontend
  • javascript
  • reusable schemas
  • runtime validation
  • secure forms
  • server-side validation
  • svelte
  • svelte components
  • sveltekit
  • typescript
  • user experience
  • validation library
  • web development
  • zod
  • zod schemas
Mar 25, 2024
Learn how to implement server-side form validation using Zod in SvelteKit applications. Enhance your web forms with robust error handling and reusable validation schemas.

Use Zod to Validate Forms on the Server with SvelteKit

In modern web development, ensuring that user inputs are correctly validated is crucial for both user experience and application security. Zod is a powerful schema validation library that integrates seamlessly with SvelteKit, enabling developers to implement robust server-side form validation with ease. This guide will walk you through setting up Zod in a SvelteKit project, creating reusable validation schemas, and handling validation errors effectively using Svelte components.

Table of Contents

  1. Introduction to Zod
  2. Installing Zod in a SvelteKit Project
  3. Creating a User Schema with Zod
  4. Understanding the safeParse Method
  5. Handling Zod Issues
  6. Implementing Server-Side Validation in SvelteKit
  7. Creating the ZodIssues Component
  8. Integrating the Validation in Svelte Components
  9. Best Practices for Form Validation
  10. Conclusion

Introduction to Zod

Zod is a TypeScript-first schema declaration and validation library that provides an intuitive API for defining and validating data structures. It excels in creating reusable validation schemas that ensure runtime type checking, enhancing both code reliability and developer productivity. Zod is widely adopted in various JavaScript and TypeScript projects for its simplicity and flexibility.

Key Features of Zod:

  • Type Inference: Automatically infers TypeScript types from schemas.
  • Composability: Easily compose complex schemas from simpler ones.
  • Error Handling: Provides detailed error messages for validation failures.
  • Transformation: Supports data transformation methods like trim and toLowerCase.

Installing Zod in a SvelteKit Project

To get started, create a new SvelteKit project and install Zod as a development dependency.

# Create a new SvelteKit project
npx create-svelte@latest my-sveltekit-app
cd my-sveltekit-app

# Install Zod as a development dependency
npm install -D zod

Creating a User Schema with Zod

Defining a validation schema is straightforward with Zod. Below is an example of a UserSchema that validates user input for email and password fields.

// src/lib/zod-schemas.ts
import { z } from "zod";

export const UserSchema = z.object({
  email: z.string().email().trim().toLowerCase(),
  password: z
    .string()
    .regex(new RegExp("^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9]).{8,}$"), {
      message:
        "Password must be at least 8 characters and contain an uppercase letter, lowercase letter, and number",
    }),
});

Key Points:

  • Email Validation: Ensures the input is a valid email, trims whitespace, and converts it to lowercase.
  • Password Validation: Enforces a minimum length and requires a mix of uppercase, lowercase, and numeric characters.
  • Reusable Schemas: The UserSchema can be reused across different forms and components for consistent validation.

Understanding the safeParse Method

Zod’s safeParse method allows for validation without throwing runtime errors, returning a result object that indicates success or failure.

const user = {
  email: "invalid@email",
  password: "invalidPassword",
};

const safeParse = UserSchema.safeParse(user);

Result:

  • Success: Contains the parsed and transformed data.
  • Failure: Provides an error object with an array of ZodIssue(s) detailing validation errors.

Handling Zod Issues

When validation fails, Zod returns detailed information about what went wrong. This information can be used to provide user-friendly error messages in your forms.

// safeParse.error.issues

[
  {
    validation: "email",
    code: "invalid_string",
    message: "Invalid email",
    path: ["email"],
  },
  {
    validation: "regex",
    code: "invalid_string",
    message:
      "Password must be at least 8 characters and contain an uppercase letter, lowercase letter, and number",
    path: ["password"],
  },
];

Components of a ZodIssue:

  • Validation: The type of validation that failed.
  • Code: A short code representing the error.
  • Message: A descriptive error message.
  • Path: The location of the error within the data structure.

Implementing Server-Side Validation in SvelteKit

SvelteKit allows you to handle form submissions and perform server-side validation seamlessly. By leveraging Zod, you can ensure that form data is validated securely on the server before processing.

Creating the Form

Start by creating a simple form in your Svelte component.

<!-- src/routes/+page.svelte -->

<form method="POST">
  <div>
    <label>
      Email
      <input type="email" name="email" required />
    </label>
  </div>

  <div>
    <label>
      Password
      <input type="password" name="password" required />
    </label>
  </div>

  <button>Sign Up</button>
</form>

Handling Form Submission on the Server

Create an action in the +page.server.ts file to handle form submissions and validate the data using Zod.

// src/routes/+page.server.ts
import { UserSchema } from "$lib/zod-schemas";
import { fail, redirect } from "@sveltejs/kit";

export const actions = {
  default: async ({ request }) => {
    // Extract form data
    const formData = await request.formData();

    // Construct user object from form data
    const user = {
      email: String(formData.get("email")),
      password: String(formData.get("password")),
    };

    // Validate using Zod's safeParse
    const safeParse = UserSchema.safeParse(user);

    // If validation fails, return errors
    if (!safeParse.success) {
      return fail(400, { issues: safeParse.error.issues });
    }

    // Proceed with user signup logic...
    // Access transformed data via safeParse.data

    // Redirect upon successful signup
    throw redirect(303, "/app");
  },
};

Key Points:

  • Form Data Extraction: Retrieves form data using the request.formData() method.
  • Validation: Utilizes UserSchema.safeParse to validate the data without throwing errors.
  • Error Handling: Returns validation issues if any, allowing the frontend to display appropriate error messages.
  • Redirection: Redirects the user upon successful form submission.

Creating the ZodIssues Component

To display validation errors to users, create a reusable Svelte component that renders the issues returned by Zod.

<!-- src/lib/components/ZodIssues.svelte -->

<script lang="ts">
  import type { ZodIssue } from "zod";

  export let issues: ZodIssue[] = [];
</script>

<ul>
  {#each issues as { message, path }}
    <li>{path[0]} - {message}</li>
  {/each}
</ul>

<style>
  ul {
    color: red;
    list-style-type: none;
    padding: 0;
  }
  li {
    margin-bottom: 0.5rem;
  }
</style>

Key Features:

  • Dynamic Rendering: Iterates over the issues array to display each error.
  • Styling: Applies basic styles to highlight errors, which can be customized further.
  • Reusability: Can be used across multiple forms and components for consistent error display.

Integrating the Validation in Svelte Components

Update your Svelte form component to utilize the ZodIssues component for displaying validation errors.

<!-- src/routes/+page.svelte -->

<script lang="ts">
  import ZodIssues from "$lib/components/ZodIssues.svelte";
  import { page } from '$app/stores';
  import { onDestroy } from 'svelte';

  let issues = [];

  // Subscribe to page data to get validation issues
  const unsubscribe = page.subscribe(($page) => {
    if ($page.data?.issues) {
      issues = $page.data.issues;
    }
  });

  onDestroy(unsubscribe);
</script>

<form method="POST">
  <div>
    <label>
      Email
      <input type="email" name="email" required />
    </label>
  </div>

  <div>
    <label>
      Password
      <input type="password" name="password" required />
    </label>
  </div>

  <button>Sign Up</button>
</form>

<!-- Display validation issues if any -->
{#if issues.length > 0}
  <ZodIssues {issues} />
{/if}

<style>
  form {
    max-width: 400px;
    margin: 0 auto;
  }
  label {
    display: block;
    margin-bottom: 1rem;
  }
  input {
    width: 100%;
    padding: 0.5rem;
    margin-top: 0.25rem;
  }
  button {
    padding: 0.75rem 1.5rem;
    background-color: #007BFF;
    border: none;
    color: white;
    cursor: pointer;
  }
  button:hover {
    background-color: #0056b3;
  }
</style>

Key Enhancements:

  • Error Display: Integrates the ZodIssues component to show validation errors beneath the form.
  • Styling: Adds basic styles to improve form aesthetics and user experience.
  • Reactive Error Handling: Subscribes to page data to dynamically update and display errors as they occur.

Best Practices for Form Validation

Implementing form validation effectively ensures that your application is both secure and user-friendly. Here are some best practices to follow:

1. Validate on Both Client and Server

  • Client-Side Validation: Provides immediate feedback to users, enhancing user experience.
  • Server-Side Validation: Ensures data integrity and security, protecting against malicious inputs.

2. Use Reusable Schemas

  • Consistency: Define reusable validation schemas with Zod to maintain consistent validation rules across your application.
  • Maintainability: Simplifies updates and maintenance by centralizing validation logic.

3. Provide Clear Error Messages

  • User-Friendly: Display descriptive and actionable error messages to guide users in correcting their inputs.
  • Accessibility: Ensure error messages are accessible to all users, including those using assistive technologies.

4. Enhance Accessibility

  • ARIA Attributes: Utilize ARIA roles and properties to make popups and error messages accessible.
  • Focus Management: Manage focus within popups to ensure smooth navigation for keyboard users.

5. Optimize Performance

  • Efficient Validation: Leverage Zod’s efficient validation methods to minimize performance overhead.
  • Lazy Loading: Load validation schemas and components only when necessary to improve initial load times.

Conclusion

Implementing server-side form validation with Zod in SvelteKit applications enhances both security and user experience. By defining reusable validation schemas, handling errors gracefully, and integrating accessible components, developers can build robust and reliable web forms that meet users’ needs effectively.

Key Benefits:

  • Enhanced Security: Prevents invalid or malicious data from entering your system.
  • Improved User Experience: Provides immediate and clear feedback, guiding users to correct their inputs.
  • Maintainable Codebase: Reusable schemas and components streamline development and maintenance.
  • Accessibility: Ensures that forms are usable by all individuals, including those with disabilities.

By following the strategies outlined in this guide, you can create accessible, secure, and high-performing forms that elevate your web development projects to the next level.