• Stars
    star
    238
  • Rank 169,306 (Top 4 %)
  • Language
    TypeScript
  • License
    MIT License
  • Created almost 3 years ago
  • Updated 12 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

This package makes it simple to use Zod with standard URLSearchParams and FormData which are typically used in Remix apps.

Remix Params Helper

All Contributors

This package makes it simple to use Zod with standard URLSearchParams and FormData which are typically used in Remix apps.

🚨 Breaking Change v0.3.0

Helper no longer requires explicit types on helper. Thanks @zolrath. This will definitely cut down on the boilerplate.

🚧 Work in progress

This package is still work in progress. I'll be refining the API and fixing the TypeScript types.

🛠 Installation

npm install remix-params-helper zod

Zod is a peer dependency

📦 Zod

Zod is used to validate untyped data and either return a valid object or a list of errors encounted.

To use the helper, first define your Zod schema. It also supports nested objects and arrays.

const ParamsSchema = z.object({
  a: z.number(),
  b: z.string(),
  c: z.boolean(),
  d: z.string().optional(),
  e: z.array(z.number()),
})

📝 API Reference

getParams(params, schema)

This function is used to parse and validate data from URLSearchParams, FormData, or Remix params object.

It returns an object that has success property. If result.success is true then result.data will be a valid object of type T, inferred from your Zod schema.

Otherwise, result.errors will be an object with keys for each property that failed validation. The key value will be the validation error message.

NOTE: Error messages will now return the message from directly Zod. You can customize the error message in your Zod schema Zod Custom Error Messages

If the validation returns multiple errors for the same key, it will return an array, otherwise it will be a string.

errors[key] = 'message'
errors[key] = ['message 1', 'message 2']

Unlike Object.fromEntries(), this function also supports multi-value keys and will convert them to an array. So e=1&e=2&e=3 will convert it to e: [1,2,3]

const url = new URL(request.url)
const result = getParams(url.searchParams, ParamsSchema)
if (!result.success) {
  throw new Response(result.errors, { status: 400 })
}
// these variables will be typed and valid
const { a, b, c, d, e } = result.data

getSearchParams(request, schema)

This helper function is used to parse and validate URLSearchParams data from the Request found in the Remix action/loader, it returns the same result values as getParams.

const result = getSearchParams(request, ParamsSchema)
if (!result.success) {
  return json(result.errors, { status: 400 })
}
// these variable will be typed and valid
const { a, b, c, d, e } = result.data

getFormData(request, schema)

This helper function is used to parse and validate FormData data from the Request found in the Remix action/loader, it returns the same result values as getParams.

const result = await getFormData(request, ParamsSchema)
if (!result.success) {
  return json(result.errors, { status: 400 })
}
// these variables will be typed and valid
const { a, b, c, d, e } = result.data

✨ New in v0.4.2 Added *OrFail() versions of the helpers

The functions getParamsOrFail(), getFormDataOrFail(), getSearchParamsOrFail() will throw an Error when parsing fails. Since the helper can only return a valid result, the return value is always the data.

// returns valid data that can be destructured or Error is thrown
const { a, b, c, d, e } = await getFormDataOrFail(request, ParamsSchema)

NOTE: Although we provide these helpers, it is recommended that you return errors instead of throwing. Form validation is typically an expected error. Throwing Error should be reserved for unexpected errors.

✨ New in v0.4.0 Support for nested objects and arrays

Input names should be dot-separated (e.g, address.street). Array names can include the square brackets (e.g., favoriteFoods[]). These are optional. The helper will correctly determine if the value is an array.

describe('test nested objects and arrays', () => {
  it('should validate nested object', () => {
    const mySchema = z.object({
      name: z.string(),
      address: z.object({
        street: z.string(),
        city: z.string(),
        state: z.string(),
        zip: z.string(),
      }),
    })
    const formData = new FormData()
    formData.set('name', 'abcdef')
    formData.set('address.street', '123 Main St')
    formData.set('address.city', 'Anytown')
    formData.set('address.state', 'US')
    formData.set('address.zip', '12345')
    const result = getParams(formData, mySchema)
    expect(result.success).toBe(true)
    expect(result.data.address.street).toBe('123 Main St')
  })
  it('should validate arrays with [] syntax', () => {
    const mySchema = z.object({
      name: z.string(),
      favoriteFoods: z.array(z.string()),
    })
    const formData = new FormData()
    formData.set('name', 'abcdef')
    formData.append('favoriteFoods[]', 'Pizza')
    formData.append('favoriteFoods[]', 'Tacos')
    formData.append('favoriteFoods[]', 'Hamburgers')
    formData.append('favoriteFoods[]', 'Sushi')
    const result = getParams(formData, mySchema)
    expect(result.success).toBe(true)
    expect(result.data.favoriteFoods?.length).toBe(4)
  })
})

useFormInputProps(schema)

This helper allows you to set the props on your form <input/> based on your Zod schema.

The function returns another function that you use to spread the properties on your input. It currently sets the following props based on the key value you specify. If you need to override any of the props, just add it after you spread.

  • name
  • type: text, number, checkbox, date, email, url
  • required: not .optional()
  • min/max: number
  • minlength/maxlength: text
  • pattern: regex

If the key doesn't exist in the schema, it will throw an error. This way if you rename any properties, it will force you to use the correct key.

This currently uses the native browser validation like required. I plan on adding enhanced client-side validation that will utilize the same Zod schema.

function Component() {
  const inputProps = useFormInputProps(schema)

  return (
    <Form>
      <input ...{inputProps('a')} />
      <input ...{inputProps('b')} />
      <input ...{inputProps('c')} />
      {/* This will throw an error since 'x' is not in schema*/}
      <input ...{inputProps('x')} />
    </Form>
  )
}

🌎 Example App

There is an example app at https://remix-params-helper.herokuapp.com/

Click on the Actions demo and the URL Params demo to see the helper in action.

😍 Contributors

Thanks goes to these wonderful people (emoji key):


Kiliman

💻 📖

Antti

💻

Matt Furden

💻

Raúl R Pearson

💻

Clifford Fajardo

📖

Benjamin

⚠️ 🐛

Dusty Doris

💻 🐛

This project follows the all-contributors specification. Contributions of any kind welcome!

More Repositories

1

operator-mono-lig

Add ligatures to Operator Mono similar to Fira Code
JavaScript
3,191
star
2

tailwindui-crawler

tailwindui-crawler downloads the component HTML files locally
JavaScript
758
star
3

remix-flat-routes

Remix package to define routes using the flat-routes convention
TypeScript
659
star
4

remix-typedjson

This package is a replacement for superjson to use in your Remix app. It handles a subset of types that `superjson` supports, but is faster and smaller.
TypeScript
432
star
5

rmx-cli

A CLI tool for Remix applications
TypeScript
178
star
6

remix-vite-template

Remix template with Vite, Tailwind CSS, and Fly.io support
TypeScript
134
star
7

shadcn-custom-theme

This tool generate a custom theme similar to the ones created by the [shadcn/ui Themes website](https://ui.shadcn.com/themes). You can specify the primary, secondary, accent, and gray colors. The color name should be one of the default Tailwind color names (e.g. red, green, blue, indigo, etc.)
JavaScript
122
star
8

remix-hydration-fix

Sample app that shows how to fix React hydration issues in a Remix app
TypeScript
112
star
9

remix-express-vite-plugin

This package includes a Vite plugin to use in your Remix app. It configures an Express server for both development and production using TypeScript.
TypeScript
101
star
10

remix-component-data

This is a proof of concept for showing how you can expose loader functions from your components to use in your routes.
TypeScript
81
star
11

remix-superjson

Sample showing how to use superjson in your Remix app and full fidelity types with inference
TypeScript
39
star
12

remix-mount-routes

Package for mounting Remix app to non-root routes
TypeScript
37
star
13

remix-scoped-params

Project showing how to track params across multiple routes by scope
TypeScript
36
star
14

remix-global-data

Example showing how to use global data in your Remix app
TypeScript
35
star
15

remix-ecommerce

Remix sample wth product catalog and shopping cart
TypeScript
32
star
16

remix-error-logging

Patch to add server side error logging support to Remix
TypeScript
23
star
17

remix-single-fetch

Remix example showing how to use new Single Data Fetch feature
TypeScript
21
star
18

remix-esbuild-analysis

Includes patch to add --metafile and bundle anaysis support to Remix compiler
15
star
19

remix-workshop

Projects used for the Remix Conf Europe Workshop
TypeScript
15
star
20

epic-stack-with-svg-sprites

Epic Stack example that shows how to use SVG sprites for your React icons
TypeScript
15
star
21

remix-server-folders

Patch to add .server extension to folder in Remix
TypeScript
14
star
22

boron-files

This app is inspired by the Carbon source-code image generator. It enables you to create and share beautiful images of your file list.
TypeScript
13
star
23

remix-vite-express

TypeScript
13
star
24

express-auth-example

TypeScript
13
star
25

epic-stack-time-zone

Epic Stack example with Time Zone client hint
TypeScript
12
star
26

kiliman-dev

TypeScript
12
star
27

remix-component-errorboundary

Example showing how to use `ComponentErrorBoundary`
TypeScript
10
star
28

strip-sourcemaps

CLI to strip server-code from sourcemaps
JavaScript
10
star
29

remix-fastify-app

Test app showing Fastify and Remix integration
JavaScript
10
star
30

remix-walletconnect

Remix example showing how to use WalletConnect with Remix
TypeScript
9
star
31

remix-suspense

Remix example showing how to ensure the Suspense fallback is rendered on route change
TypeScript
9
star
32

remix-blog-mongodb

Remix example that updates the blog tutorial to use MongoDB and Quill
JavaScript
9
star
33

remix-ras-server-example

Sample Remix App Server with custom server file
TypeScript
8
star
34

mono-symbolicate-helper

Helper service to convert obfuscated stack traces to meaningful ones using mono-symbolicate
C#
8
star
35

remix-build-error

A sample app with patches to display build errors in the browser
TypeScript
8
star
36

MvxCommandToMessage

Sample showing how to use a value converter to convert a command into a message
C#
6
star
37

remix-vite-mui

Remix+VIte+MUI example
TypeScript
6
star
38

remix-playground

TypeScript
6
star
39

remix-streams

TypeScript
4
star
40

remix-ftp-deploy

Example to show how to bundle and FTP your Remix app to a host that doesn't let you build
TypeScript
4
star
41

remix-json-fetcher

TypeScript
4
star
42

NuForVS

Nu for Visual Studio
C#
4
star
43

mvx-samples

Some sample apps for MvvmCross
C#
3
star
44

remix-upload

TypeScript
3
star
45

remix-typedjson-example

TypeScript
3
star
46

remix-flat-routes-example

Example site showing the Remix flat routes routing convention
TypeScript
2
star
47

remix-1.15.0-vercel

Remix v1.15.0 Vercel Template
TypeScript
2
star
48

remix-root-errorboundary

Remix example showing root ErrorBoundary
TypeScript
2
star
49

MicrowDB

A mini PCL version of RavenDB client for mobile. Uses SQLite.
C#
2
star
50

epic-stack-theme

Epic Stack example showing how to customize the theme using shadcn-custom-theme tool
TypeScript
2
star
51

remix-js-cookie

Sample showing how to use client-side cookies in Remix
TypeScript
2
star
52

remix-vite-2.2.0

Sample repo using unstable Vite support in Remix
TypeScript
2
star
53

remix-drawer

Example app showing Drawer navigation UI
TypeScript
2
star
54

remix-mount-routes-example

Example showing remix app mounted to non-root URL
TypeScript
1
star
55

epic-stack-kiliman

1
star
56

remix-indie-typedjson

TypeScript
1
star
57

MvxEventsSample

Sample on how to use MvvmCross MvxMessenger plugin with typed Events class
C#
1
star
58

remix-vscode-themes

1
star
59

remix-dnd

JavaScript
1
star
60

remix-docker-test

JavaScript
1
star
61

remix-vite-params

Test repo using Vite and remix-params-helper
TypeScript
1
star
62

remix-vite-2.4.1-pre.2

JavaScript
1
star
63

remix-mapbox

TypeScript
1
star
64

remix-vite-basic

Basic Remix+Vite template with React Canary
TypeScript
1
star
65

remix-custom-domain

Remix example showing how to map custom domain to route param
TypeScript
1
star