npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@adimis/react-formix

v0.1.1

Published

Welcome to `@adimis/react-formix`, the ultimate React library for creating dynamic, schema-based forms. Built with modern web development in mind, this library harnesses the power of React, Zod for robust schema validation, and react-hook-form for seamles

Downloads

124

Readme

@adimis/react-formix

Welcome to @adimis/react-formix, the ultimate React library for creating dynamic, schema-based forms. Built with modern web development in mind, this library harnesses the power of React, Zod for robust schema validation, and react-hook-form for seamless form state management. Whether you need to build custom forms from the ground up or use prebuilt components for rapid deployment, @adimis/react-formix is your go-to solution.

Key Features

  • Schema-Based Form Generation: Define your form fields and validation rules using a JSON schema.
  • Zod-Powered Validation: Leverage Zod for strong data validation and meaningful feedback.
  • Flexible Layout Options: Create responsive and dynamic form layouts effortlessly.
  • Conditional Rendering: Dynamically show or hide form fields based on other field values.
  • State Persistence: Save and restore form state using local storage or session storage.
  • Custom Render Functions: Specify custom render functions inside the schema array's object.
  • TypeScript Support: Utilize generic TypeScript typing for form field types.
  • Dynamic Field Visibility: Update field visibility in real-time based on user input.
  • Dynamic Validation: Remove field validations dynamically based on specific conditions.
  • Developer Tools: Integrate developer tools for debugging and inspecting form state.
  • Customizable Themes: Adapt form themes to align with your application's design.
  • Latest React and Next.js Support: Ensure compatibility with modern web development practices.

Installation and Setup

  1. Install the package via npm:

    npm install @adimis/react-formix
  2. Import the style in your main.tsx or index.tsx file:

    import "@adimis/react-formix/dist/style.css";
  3. Install and setup Tailwind CSS: Tailwind CSS Docs

  4. Add the following to your index.css file:

    @tailwind base;
    @tailwind components;
    @tailwind utilities;
    
    @layer base {
      :root {
        --background: hsl(0, 0%, 100%);
        --foreground: hsl(222.2, 84%, 4.9%);
        --card: hsl(0, 0%, 100%);
        --card-foreground: hsl(222.2, 84%, 4.9%);
        --popover: hsl(0, 0%, 100%);
        --popover-foreground: hsl(222.2, 84%, 4.9%);
        --primary: hsl(221.2, 83.2%, 53.3%);
        --primary-foreground: hsl(210, 40%, 98%);
        --secondary: hsl(210, 40%, 96.1%);
        --secondary-foreground: hsl(222.2, 47.4%, 11.2%);
        --muted: hsl(210, 40%, 96.1%);
        --muted-foreground: hsl(215.4, 16.3%, 46.9%);
        --accent: hsl(210, 40%, 96.1%);
        --accent-foreground: hsl(222.2, 47.4%, 11.2%);
        --destructive: hsl(0, 84.2%, 60.2%);
        --destructive-foreground: hsl(210, 40%, 98%);
        --border: hsl(214.3, 31.8%, 91.4%);
        --input: hsl(214.3, 31.8%, 91.4%);
        --ring: hsl(221.2, 83.2%, 53.3%);
        --radius: 1rem;
      }
    
      .dark {
        --background: hsl(222.2, 84%, 4.9%);
        --foreground: hsl(210, 40%, 98%);
        --card: hsl(222.2, 84%, 4.9%);
        --card-foreground: hsl(210, 40%, 98%);
        --popover: hsl(222.2, 84%, 4.9%);
        --popover-foreground: hsl(210, 40%, 98%);
        --primary: hsl(217.2, 91.2%, 59.8%);
        --primary-foreground: hsl(222.2, 47.4%, 11.2%);
        --secondary: hsl(217.2, 32.6%, 17.5%);
        --secondary-foreground: hsl(210, 40%, 98%);
        --muted: hsl(217.2, 32.6%, 17.5%);
        --muted-foreground: hsl(215, 20.2%, 65.1%);
        --accent: hsl(217.2, 32.6%, 17.5%);
        --accent-foreground: hsl(210, 40%, 98%);
        --destructive: hsl(0, 62.8%, 30.6%);
        --destructive-foreground: hsl(210, 40%, 98%);
        --border: hsl(217.2, 32.6%, 17.5%);
        --input: hsl(217.2, 32.6%, 17.5%);
        --ring: hsl(224.3, 76.3%, 48%);
      }
    }
    
    @layer base {
      * {
        @apply border-border;
      }
    
      body {
        @apply bg-background text-foreground;
      }
    }
  5. Install required dependencies:

    npm i tailwindcss-animate
  6. Setup your tsconfig.config.js file:

    /** @type {import('tailwindcss').Config} */
    export default {
      darkMode: ["class"],
      content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
      theme: {
        container: {
          center: true,
          padding: "2rem",
          screens: {
            "2xl": "1400px",
          },
        },
        extend: {
          colors: {
            border: "var(--border)",
            input: "var(--input)",
            ring: "var(--ring)",
            background: "var(--background)",
            foreground: "var(--foreground)",
            primary: {
              DEFAULT: "var(--primary)",
              foreground: "var(--primary-foreground)",
            },
            secondary: {
              DEFAULT: "var(--secondary)",
              foreground: "var(--secondary-foreground)",
            },
            destructive: {
              DEFAULT: "var(--destructive)",
              foreground: "var(--destructive-foreground)",
            },
            muted: {
              DEFAULT: "var(--muted)",
              foreground: "var(--muted-foreground)",
            },
            accent: {
              DEFAULT: "var(--accent)",
              foreground: "var(--accent-foreground)",
            },
            popover: {
              DEFAULT: "var(--popover)",
              foreground: "var(--popover-foreground)",
            },
            card: {
              DEFAULT: "var(--card)",
              foreground: "var(--card-foreground)",
            },
          },
          borderRadius: {
            lg: "var(--radius)",
            md: "calc(var(--radius) - 2px)",
            sm: "calc(var(--radius) - 4px)",
          },
          keyframes: {
            "accordion-down": {
              from: { height: "0" },
              to: { height: "var(--radix-accordion-content-height)" },
            },
            "accordion-up": {
              from: { height: "var(--radix-accordion-content-height)" },
              to: { height: "0" },
            },
          },
          animation: {
            "accordion-down": "accordion-down 0.2s ease-out",
            "accordion-up": "accordion-up 0.2s ease-out",
          },
        },
      },
      plugins: [require("tailwindcss-animate")],
    };

Usage

Here's an example of how to create a basic signup form using @adimis/react-formix:

"use client";

import { z } from "zod";
import { Input } from "./components/input";
import { Button } from "./components/button";
import {
  FormBody,
  FormContent,
  FormixFormProvider,
  FormDescription,
  FormFlexFields,
  FormFooter,
  FormHeader,
  FormTitle,
  ISchemaFormProps,
  ThemeProvider,
} from "@adimis/react-formix"; // 120.1k (gzipped: 34.2k)
import "@adimis/react-formix/dist/style.css";

interface SignUp {
  username: string;
  email: string;
  address: string;
  phone: string;
  password: string;
  gender: string;
  terms: boolean;
  file: File;
  date: Date;
  year: number;
  expertise: string[];
}

const App = () => {
  const schemaFormProps: ISchemaFormProps<SignUp> = {
    formLabel: "Example Barebone Form",
    formSlug: "example-barebone-form",
    persistFormResponse: "localStorage",
    devTools: true,
    formDisabled: false,
    enableConditionalRendering: true,
    schema: [
      {
        key: "username",
        label: "Username",
        description: "Enter your desired username.",
        autoComplete: "username",
        type: "text",
        placeholder: "Your username",
        defaultValue: "",
        validations: z
          .string()
          .min(1, "Username is required")
          .max(20, "Username must not exceed 20 characters"),
        render: ({
          formDisabled,
          formItem,
          formMethods,
          submitButtonLoading,
        }) => (
          <Input
            type={formItem.type}
            id={formItem.key}
            disabled={formDisabled || submitButtonLoading}
            style={{
              width: "100%",
              height: "10px",
              border: "1px solid #ccc",
              borderRadius: "5px",
              padding: "10px",
              margin: "5px 0",
            }}
            {...formMethods.register(formItem.key)}
          />
        ),
      },
      {
        key: "email",
        label: "Email",
        description: "Enter your email address.",
        autoComplete: "email",
        type: "email",
        placeholder: "Your email",
        defaultValue: "",
        validations: z
          .string()
          .email("Enter a valid email address")
          .min(1, "Email is required"),
        render: ({
          formDisabled,
          formItem,
          formMethods,
          submitButtonLoading,
        }) => (
          <Input
            type={formItem.type}
            id={formItem.key}
            disabled={formDisabled || submitButtonLoading}
            style={{
              width: "100%",
              height: "10px",
              border: "1px solid #ccc",
              borderRadius: "5px",
              padding: "10px",
              margin: "5px 0",
            }}
            {...formMethods.register(formItem.key)}
          />
        ),
      },
      {
        key: "address",
        label: "Address",
        description: "Enter your full address.",
        autoComplete: "address-line1",
        type: "text",
        placeholder: "Your address",
        defaultValue: "",
        validations: z
          .string()
          .min(10, "Address should be at least 10 characters"),
        render: ({
          formDisabled,
          formItem,
          formMethods,
          submitButtonLoading,
        }) => (
          <Input
            type={formItem.type}
            id={formItem.key}
            disabled={formDisabled || submitButtonLoading}
            style={{
              width: "100%",
              height: "10px",
              border: "1px solid #ccc",
              borderRadius: "5px",
              padding: "10px",
              margin: "5px 0",
            }}
            {...formMethods.register(formItem.key)}
          />
        ),
      },
      {
        key: "phone",
        label: "Phone",
        description: "Enter your phone number with country code.",
        autoComplete: "tel",
        type: "tel",
        placeholder: "+1234567890",
        defaultValue: "",
        validations: z
          .string()
          .regex(/^\+?(\d.*){10,}$/, "Enter a valid phone number"),
        render: ({
          formDisabled,
          formItem,
          formMethods,
          submitButtonLoading,
        }) => (
          <Input
            type={formItem.type}
            id={formItem.key}
            disabled={formDisabled || submitButtonLoading}
            style={{
              width: "100%",
              height: "10px",
              border: "1px solid #ccc",
              borderRadius: "5px",
              padding: "10px",
              margin: "5px 0",
            }}
            {...formMethods.register(formItem.key)}
          />
        ),
      },
      {
        key: "password",
        label: "Password",
        description: "Enter a strong password.",
        autoComplete: "new-password",
        type: "password",
        placeholder: "Your password",
        defaultValue: "",
        validations: z
          .string()
          .min(8, "Password should be at least 8 characters")
          .max(20, "Password must not exceed 20 characters"),
        displayConditions: [
          {
            dependentField: "email",
            dependentFieldValue: "[email protected]",
            operator: "===",
          },
        ],
        removeValidationConditions: [
          {
            dependentField: "email",
            dependentFieldValue: "[email protected]",
            operator: "!==",
          },
        ],
        render: ({
          formDisabled,
          formItem,
          formMethods,
          submitButtonLoading,
        }) => (
          <Input
            type={formItem.type}
            id={formItem.key}
            disabled={formDisabled || submitButtonLoading}
            style={{
              width: "100%",
              height: "10px",
              border: "1px solid #ccc",
              borderRadius: "5px",
              padding: "10px",
              margin: "5px 0",
            }}
            {...formMethods.register(formItem.key)}
          />
        ),
      },
    ],
    defaultValues: {
      email: "[email protected]",
      phone: "919625183597",
    },
    onSubmit: (values) =>
      console.log(
        "On Submit Example Form Response: ",
        JSON.stringify(values, null, 4)
      ),
    onInvalidSubmit: (values) =>
      console.log(
        "On Submit Invalid Example Form Response: ",
        JSON.stringify(values, null, 4)
      ),
  };

  return (
    <ThemeProvider defaultTheme="dark">
      <FormixFormProvider {...schemaFormProps}>
        <FormBody>
          <FormHeader>
            <FormTitle />
            <FormDescription />
          </FormHeader>
          <FormContent>
            <FormFlexFields fluid columns={2} />
          </FormContent>
          <FormFooter>
            <Button type="submit" className="mt-5">
              Submit
            </Button>
          </FormFooter>
        </FormBody>
      </FormixFormProvider>
    </ThemeProvider>
  );
};

export default App;

Exports

Core Components

FormixProvider

Wraps the form and provides the Formix context to its children. This component initializes the form schema and context, making it accessible to all nested components.

useFormix

A hook for accessing the Formix context. It provides access to the form's state and methods, enabling high-level interaction with the form.

Styled Components

FormixFormProvider

The primary context component that utilizes the form schema and methods. It offers access to form state and functions for seamless form interaction.

FormBody

The main body of the form. It wraps all form elements and handles form submission, maintaining a consistent structure and layout.

FormHeader

The header section of the form, used to display the form title and description, providing clear context for the form's purpose.

FormFooter

The footer section of the form, typically used for submit buttons or other form actions, ensuring user actions are clearly presented at the end of the form.

FormContent

The content section of the form, wrapping the main form fields and organizing them within the form's structure.

FormTitle

Displays the form title. This component is used within FormHeader to show the main heading of the form.

FormDescription

Displays the form description. This component is used within FormHeader to provide additional context or instructions for the form.

FormFlexFields

Renders form fields in a flexible grid layout, helping in creating responsive forms with customizable grid settings such as column count and gap size.

FormField

Wraps individual form fields and connects them to the form context using react-hook-form's Controller component to manage field state and validation.

FieldItem

A wrapper for individual form items, providing a consistent structure and context for form fields.

FieldLabel

Renders the label for a form field, ensuring that labels are consistently styled and correctly associated with their corresponding input elements.

FieldControl

Renders the control element (input, select, etc.) for a form field, managing the state and attributes necessary for the field's functionality and accessibility.

FieldDescription

Renders the description for a form field, providing additional information or instructions about the field's purpose or expected input.

FieldErrorMessage

Displays error messages for form fields, showing validation errors to help users correct their inputs.

useFormField

A hook for accessing the form field context and state, providing field-level information and methods for interacting with the form.

ThemeProvider

Provides theme context to its children, allowing for dynamic theme changes and customization across the application.

useTheme

A hook for accessing the theme context, providing the current theme and a function to update the theme.

Contributing

We welcome contributions to @adimis/react-formix. If you have suggestions for new features, improvements, or bug fixes, please open an issue or submit a pull request on our GitHub repository.

License

This project is licensed under the GPL-3.0-only License. See the LICENSE file for details.

Additional Information

For detailed documentation, refer to the docs folder. Open the index.html file located at node_modules/@adimis/react-formix/docs/index.html in a live server to access the documentation.