Coder Social home page Coder Social logo

ozef's Introduction

Opinionated Zod-empowered forms

Introduction

Ozef is an opinionated library that aims to guarantee type-safe, declarative forms with minimal boilerplate. It is built on top of Zod which provides a powerful type system for validating data.

Features Ozef supports:

  • Guaranteed type-safety in onSubmit
  • async onSubmit
  • Declarative forms
  • Validation checking
  • Input types like radio and select

Ozef is not a component library. It does not provide any pre-built components. Instead, plug in existing components to build forms.

Ozef lets you build forms like:

import Input from "./CustomInput";

const NewFlowForm = ozef({
  schema: z.object({
    name: z.string().min(3),
    email: z.string().email(),
    // for radios
    favoriteColor: z.enum(["Red", "Blue"]),
    // for selects
    favoriteColor: z.union([z.literal("Red"), z.literal("Blue")]),
  }),
  // Plug in your own input components
  Input,
  // Define components for each type
  InputRadio: ({ radioValue, ...props }) => (
    <div>
      {radioValue}
      <input type="radio" {...props} />
    </div>
  ),
  // Error labels
  Error: ({ error }) => <span className="text-red-500">{error}</span>,
  Submit: ({ submitting }) => (
    <RoundedButton type="submit" loading={submitting}>
      Submit
    </RoundedButton>
  ),
});

<NewFlowForm
  className="flex flex-col gap-2"
  onSubmit={async (vals) => {
    /** vals has the type { name: string, email: string, favoriteColor: "Red" | "Blue" } */
    ...
  }}
>
  <NewFlowForm.Field.Name prefixIcon="icon_1" />
  <NewFlowForm.Error.Name />

  <NewFlowForm.Field.Email />
  <NewFlowForm.Error.Email />

  <NewFlowForm.Error.FavoriteColor />
  <NewFlowForm.Field.FavoriteColor>
    <NewFlowForm.Field.FavoriteColor.Blue />
    <NewFlowForm.Field.FavoriteColor.Red />
  </NewFlowForm.Field.FavoriteColor>

  <NewFlowForm.Event.Submit />
  <NewFlowForm.Error.Submission error="Please fill out the form" />
</NewFlowForm>

with full type-script support!

Installation

Ozef has minimal dependencies (just Zod and Jotai) and is easy to install.

npm i [email protected] jotai ozef

Usage

Basic usage

import ozef from "ozef";

const Form = ozef({
  schema: z.object({
    name: z.string().min(3),
    email: z.string().email(),
  }),
});

const SomeComponent = () => {
  return (
    <Form
      onSubmit={async (vals) => {
        // vals is guaranteed to be of type { name: string, email: string }
        ...
      }}
    >
      // Use `Field` components to render inputs for the form
      <Form.Field.Name />
      <Form.Field.Email />

      // Use `Error` components to render error labels
      <Form.Error.Name />

      // Use `Event` components to render special user events components
      <Form.Event.Submit />
    </Form>
  );
};

Ozef Input Components

Components need to modified before being able to be used with Ozef. This is because Ozef needs to be able to pass certain props to the components.

import { type OzefInputProps } from "ozef";

type InputProps = OzefInputProps & {
  // Add your own props
  prefixIcon?: string;
};

const Input = ({ prefixIcon, hasError, ...props }: InputProps) => {
  return (
    <div
      className={`${
        props.className
      } ${hasError ? "focus-within:ring-red-500" : ""}`}
    >
      {prefixIcon && (
        <MaterialsIcon className="!text-xl text-zinc-500" icon={prefixIcon} />
      )}
      <input
        {/* This is the important part. Ozef needs to pass props to the native input component. */}
        {...props}
        className="..."
      />
    </div>
  );
};

Defaults

import ozef from "ozef";

const Form = ozef({
  schema: z.object({
    name: z.string().min(3),
    email: z.string().email(),
  }),
  defaults: {
    name: "John Doe",
    email: "[email protected]"
  }
});

const SomeComponent = () => {
  return (
    <Form
      onSubmit={async (vals) => {
        // vals is guaranteed to be of type { name: string, email: string }
        ...
      }}
    >
      // Use `Field` components to render inputs for the form
      <Form.Field.Name />
      <Form.Field.Email />

      // Use `Error` components to render error labels
      <Form.Error.Name />

      // Use `Event` components to render special user events components
      <Form.Event.Submit />
    </Form>
  );
};

Variable defaults

If you're passing down a variable as defaults, you'll need to use useMemo to prevent the form from re-rendering every time the parent component re-renders.

import ozef from "ozef";
import { useMemo } from "react";

const SomeComponent = ({defaults}: Props) => {
  const Form = useMemo(() => ozef({
    schema: z.object({
      name: z.string().min(3),
      email: z.string().email(),
    }),
    defaults
  }), [defaults])

  return (
    <Form
      onSubmit={async (vals) => {
        // vals is guaranteed to be of type { name: string, email: string }
        ...
      }}
    >
      // Use `Field` components to render inputs for the form
      <Form.Field.Name />
      <Form.Field.Email />

      // Use `Error` components to render error labels
      <Form.Error.Name />

      // Use `Event` components to render special user events components
      <Form.Event.Submit />
    </Form>
  );
};

shadcn/Radix Select example

import ozef from "ozef";

const UpdateRewriteSettingsForm = ozef({
  schema: z.object({
    terseness: z.enum(["short", "medium", "long"]),
  }),
});

const ShadcnExample = () => {
  // getter hook
  const terseness = UpdateRewriteSettingsForm.Field.Terseness.useValue();

  return (
    <UpdateRewriteSettingsForm
      onSubmit={() => {
        // handler
      }}
    >
      <Select
        value={terseness}
        // setter function
        onValueChange={UpdateRewriteSettingsForm.Field.Terseness.setValue}
      >
        <div className="space-y-1">
          <Label>Terseness</Label>
          <SelectTrigger>
            <SelectValue />
          </SelectTrigger>
          <SelectContent>
            <UpdateRewriteSettingsForm.Field.Terseness.Short />
            <UpdateRewriteSettingsForm.Field.Terseness.Medium />
            <UpdateRewriteSettingsForm.Field.Terseness.Long />
          </SelectContent>
        </div>
      </Select>
    </UpdateRewriteSettingsForm>
  );
};

ozef's People

Contributors

lanc33llis avatar

Stargazers

ethan avatar Ethan Niser avatar Sturle Spetland avatar  avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.