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 🙏

© 2026 – Pkg Stats / Ryan Hefner

tac-form

v1.0.26

Published

The JSON Schema-based library for creating forms.

Readme

tac-form

tac-form is a powerful, JSON Schema-based library for creating dynamic forms in React applications. It offers a flexible and intuitive way to generate, manage, and validate forms with minimal boilerplate.

Table of Contents

  1. Key Features
  2. Installation
  3. Basic Usage
  4. Form Configuration
  5. Field Types
  6. Conditional Rendering
  7. Validation
  8. External Component Integration
  9. Context and Form Management
  10. Theming and Styling
  11. Advanced Examples
  12. API Reference
  13. Contributing
  14. License

Key Features

  • Dynamic form generation based on JSON configuration
  • Support for multiple field types
  • Conditional field rendering
  • Form-level and field-level validation
  • Integration with external UI libraries
  • Dark mode support
  • Multi-form management via React Context

Installation

  1. Set up Tailwind CSS (if not already configured in your project)

  2. Install the library:

npm install tac-form
  1. Copy the component to your working directory:
npx tac-form add

Basic Usage

Here's a simple example of how to use tac-form:

import React from 'react';
import { DynamicForm, FormProvider } from 'tac-form';

const App = () => {
  const formConfig = {
    form: {
      id: 'simpleForm',
      submitText: 'Submit',
      onSubmit: (data) => {
        console.log('Form submitted:', data);
      },
    },
    fields: [
      {
        name: 'email',
        label: 'Email',
        type: 'email',
      },
      {
        name: 'password',
        label: 'Password',
        type: 'password',
      },
    ],
  };

  return (
    <FormProvider>
      <DynamicForm id="simpleForm" config={formConfig} />
    </FormProvider>
  );
};

export default App;

Form Configuration

The FormConfig object is the core of tac-form. It defines the structure and behavior of your form:

interface FormConfig<T extends Record<string, unknown>> {
  form: {
    id: string;
    submitText: ReactNode | string;
    onSubmit: (data: T) => void;
  };
  fields: FieldInput<T>[];
}

Field Types

tac-form supports various field types out of the box:

| Field Type | Component | Description | |------------|---------------|---------------------------| | text | TextInput | Standard text input | | textarea | TextareaInput | Multi-line text input | | number | NumberInput | Numeric input | | email | EmailInput | Email input | | select | SelectInput | Dropdown selection | | radio | RadioInput | Radio button group | | checkbox | CheckboxInput | Checkbox or checkbox group| | phone | PhoneInput | Phone number input | | date | DateInput | Date picker | | readonly | ReadonlyInput | Display-only field |

Example of different field types:

const formConfig = {
  form: {
    id: 'multiFieldForm',
    submitText: 'Submit',
    onSubmit: (data) => console.log(data),
  },
  fields: [
    {
      name: 'fullName',
      label: 'Full Name',
      type: 'text',
    },
    {
      name: 'bio',
      label: 'Biography',
      type: 'textarea',
    },
    {
      name: 'age',
      label: 'Age',
      type: 'number',
    },
    {
      name: 'gender',
      label: 'Gender',
      type: 'select',
      options: [
        { label: 'Male', value: 'male' },
        { label: 'Female', value: 'female' },
        { label: 'Other', value: 'other' },
      ],
    },
    {
      name: 'newsletter',
      label: 'Subscribe to newsletter',
      type: 'checkbox',
    },
  ],
};

Conditional Rendering

You can conditionally render fields based on the values of other fields:

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'hasChildren',
      label: 'Do you have children?',
      type: 'radio',
      options: [
        { label: 'Yes', value: 'yes' },
        { label: 'No', value: 'no' },
      ],
    },
    {
      name: 'numberOfChildren',
      label: 'Number of children',
      type: 'number',
      dependency: {
        on: ['hasChildren'],
        condition: (values) => values.hasChildren === 'yes',
      },
    },
  ],
};

Validation

tac-form integrates with Zod for schema validation:

import { z } from 'zod';

const schema = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
});

const FormWithValidation = () => {
  const formConfig = {
    // ... form config
  };

  return <DynamicForm id="validatedForm" config={formConfig} schema={schema} />;
};

Note: schema is optional. If not provided, the form will not be validated. but you have to pass the schema for all the fields. and if field is atleast add z.string().min(1) to it as, field uses '' as default value .

External Component Integration

You can integrate external UI components:

  • External library component or Custom component are also supported having name, value and onChange as required props.
import { TextField } from '@mui/material';

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'customInput',
      label: 'Custom Input',
      type: 'text',
      component: TextField,
    },
  ],
};

Context and Form Management

The FormProvider allows you to manage multiple forms:

import { useFormContext } from 'tac-form';


const ParentComponent = () => {
  const handleClick = (id: string) => {
  const value = getFormValue(id, 'textField')
    console.log({ value })
  }
  return (
      <DynamicForm id="myForm" config={formConfig} />
  );
};

Theming and Styling

tac-form supports dark mode and custom styling:

const formConfig = {
  // ... other config
  fields: [
    {
      name: 'styledInput',
      label: 'Styled Input',
      type: 'text',
      customClassName: {
        container: 'my-custom-container',
        input: 'my-custom-input',
        label: 'my-custom-label',
      },
      styles: {
        input: { borderColor: 'blue' },
      },
    },
  ],
};

const ThemedForm = () => {
  return <DynamicForm id="themedForm" config={formConfig} darkMode={true} />;
};

Advanced Examples

Typescript Form


type testFormConfig = {
	textField: string,
	readOnly: string,
	textareaField: string,
	numberField: number,
	selectField: string,
	radioField: string,
	checkboxField: string[],
	phoneField: string,
	dateField: string,
}
const testFormConfig: FormConfig<testFormConfig> = {
	form: {
		id: 'test',
		submitText: 'submit',
		onSubmit: (data) => {
			console.log(data)
		}
	},
	fields: [
	{
		name: 'textField',
		label: 'Text Field',
		type: 'text',
		placeholder: 'Enter text',
	},
	{
		name: 'readOnly',
		label: 'read only',
		type: 'readonly',
		placeholder: 'Enter text',
		value: 'Sample text',
	},
	{
		name: 'textareaField',
		label: 'Textarea Field',
		type: 'textarea',
		placeholder: 'Enter detailed text',
		value: 'Sample textarea content',
	},
	{
		name: 'numberField',
		label: 'Number Field',
		type: 'number',
		placeholder: 'Enter a number',
		value: 12345,
	},
	{
		name: 'selectField',
		label: 'Select Field',
		type: 'select',
		options: [
			{ label: 'Option 1', value: 'option1' },
			{ label: 'Option 2', value: 'option2' },
			{ label: 'Option 3', value: 'option3' }
		],
		placeholder: 'slec',
		value: 'option2',
	},
	{
		name: 'radioField',
		label: 'Radio Field',
		type: 'radio',
		options: [
			{ label: 'Radio 1', value: 'radio1' },
			{ label: 'Radio 2', value: 'radio2' },
			{ label: 'Radio 3', value: 'radio3' }
		],
		value: 'radio1',
	},
	{
		name: 'checkboxField',
		label: 'Checkbox Field',
		type: 'checkbox',
		options: [
			{ label: 'Checkbox 1', value: 'checkbox1' },
			{ label: 'Checkbox 2', value: 'checkbox2' },
			{ label: 'Checkbox 3', value: 'checkbox3' }
		],
		value: ['checkbox3', 'checkbox1'],
	},
	{
		name: 'phoneField',
		label: 'Phone Number Field',
		type: 'phone',
		placeholder: 'Enter phone number',
		value: '918340453292',
	},
	{
		name: 'dateField',
		label: 'Date Field',
		type: 'date',
		placeholder: 'Select date',
		value: '2023-08-27',
	},
	]
}

const testFormSchema = z.object({
	textField: z.string().min(8, { message: 'Minimum 8 characters required' }),
	readOnly: z.string(),
	textareaField: z.string(),
	numberField: z.number(),
	selectField: z.string(),
	radioField: z.string(),
	checkboxField: z.array(z.string()),
	phoneField: z.string(),
	dateField: z.string().or(z.array(z.date())),
})


const Home = () => {
	const { forms, getFormValue, addForm } = useFormContext<testFormConfig>()

	const handleClick = (id: string) => {
		const value = getFormValue(id, 'textField')
		console.log({ value })
	}
	return (
		<>
		<button onClick={() => handleClick('2')}>	click me	</button>
		<div className='w-full dark h-[100vh] flex justify-center items-center' >
			<div className='w-[80%] h-auto bg-white dark:bg-slate-600 rounded-lg px-10 py-4'>
				<DynamicForm
					id='2'
				config={testFormConfig}
				schema={testFormSchema}
				/>
			</div>
		</div>
		</>
	)
}

export default Home

Multi-step Form

import React, { useState } from 'react';
import { DynamicForm, FormProvider } from 'tac-form';

const MultiStepForm = () => {
  const [step, setStep] = useState(1);

  const stepOneConfig = {
    form: {
      id: 'stepOne',
      submitText: 'Next',
      onSubmit: (data) => {
        console.log('Step One Data:', data);
        setStep(2);
      },
    },
    fields: [
      { name: 'firstName', label: 'First Name', type: 'text' },
      { name: 'lastName', label: 'Last Name', type: 'text' },
    ],
  };

  const stepTwoConfig = {
    form: {
      id: 'stepTwo',
      submitText: 'Submit',
      onSubmit: (data) => {
        console.log('Step Two Data:', data);
        // Handle final submission
      },
    },
    fields: [
      { name: 'email', label: 'Email', type: 'email' },
      { name: 'phone', label: 'Phone', type: 'phone' },
    ],
  };

  return (
    <FormProvider>
      {step === 1 && <DynamicForm id="stepOne" config={stepOneConfig} />}
      {step === 2 && <DynamicForm id="stepTwo" config={stepTwoConfig} />}
    </FormProvider>
  );
};

export default MultiStepForm;

Form with Complex Validation

import { z } from 'zod';
import { DynamicForm } from 'tac-form';

const complexSchema = z.object({
  username: z.string().min(3, 'Username must be at least 3 characters'),
  email: z.string().email('Invalid email address'),
  password: z.string().min(8, 'Password must be at least 8 characters'),
  confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
  message: "Passwords don't match",
  path: ['confirmPassword'],
});

const ComplexForm = () => {
  const formConfig = {
    form: {
      id: 'complexForm',
      submitText: 'Register',
      onSubmit: (data) => console.log('Form submitted:', data),
    },
    fields: [
      { name: 'username', label: 'Username', type: 'text' },
      { name: 'email', label: 'Email', type: 'email' },
      { name: 'password', label: 'Password', type: 'password' },
      { name: 'confirmPassword', label: 'Confirm Password', type: 'password' },
    ],
  };

  return <DynamicForm id="complexForm" config={formConfig} schema={complexSchema} />;
};

export default ComplexForm;

Future Work

  • Otp Form
  • Native Multi Step Form
  • File Upload Form
  • Image Crop Form
  • Signature Form

License

tac-form is released under the MIT License.