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.
Install the package via npm:
npm install @adimis/react-formix
Import the style in your main.tsx or index.tsx file:
import "@adimis/react-formix/dist/style.css";
Install and setup Tailwind CSS: Tailwind CSS Docs
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;
}
}
Install required dependencies:
npm i tailwindcss-animate
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")],
};
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: "admin@adimis.in",
operator: "===",
},
],
removeValidationConditions: [
{
dependentField: "email",
dependentFieldValue: "admin@adimis.in",
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: "adimis.ai.001@gmail.com",
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;
FormixProviderWraps 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.
useFormixA hook for accessing the Formix context. It provides access to the form's state and methods, enabling high-level interaction with the form.
FormixFormProviderThe primary context component that utilizes the form schema and methods. It offers access to form state and functions for seamless form interaction.
FormBodyThe main body of the form. It wraps all form elements and handles form submission, maintaining a consistent structure and layout.
FormHeaderThe header section of the form, used to display the form title and description, providing clear context for the form's purpose.
FormFooterThe 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.
FormContentThe content section of the form, wrapping the main form fields and organizing them within the form's structure.
FormTitleDisplays the form title. This component is used within FormHeader to show the main heading of the form.
FormDescriptionDisplays the form description. This component is used within FormHeader to provide additional context or instructions for the form.
FormFlexFieldsRenders form fields in a flexible grid layout, helping in creating responsive forms with customizable grid settings such as column count and gap size.
FormFieldWraps individual form fields and connects them to the form context using react-hook-form's Controller component to manage field state and validation.
FieldItemA wrapper for individual form items, providing a consistent structure and context for form fields.
FieldLabelRenders the label for a form field, ensuring that labels are consistently styled and correctly associated with their corresponding input elements.
FieldControlRenders the control element (input, select, etc.) for a form field, managing the state and attributes necessary for the field's functionality and accessibility.
FieldDescriptionRenders the description for a form field, providing additional information or instructions about the field's purpose or expected input.
FieldErrorMessageDisplays error messages for form fields, showing validation errors to help users correct their inputs.
useFormFieldA hook for accessing the form field context and state, providing field-level information and methods for interacting with the form.
ThemeProviderProvides theme context to its children, allowing for dynamic theme changes and customization across the application.
useThemeA hook for accessing the theme context, providing the current theme and a function to update the theme.
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.
This project is licensed under the GPL-3.0-only License. See the LICENSE file for details.
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.