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

@kbgarcia8/react-dynamic-form

v2.0.4

Published

A form that can be nested with editable, expandable and flexible input-forms

Downloads

29

Readme

react-dynamic-form

Reusable React dynamic form showcasing editable, expandable and flexible inputs

npm version downloads

Table of Contents

Features

  • 🖌️ Themeing support via styled components
  • 🆗 Typescript support
  • ↔️ Expandable and editable via nested form structure
  • 🧩 Ease of integration in any React project

Installation

npm install @kbgarcia8/react-dynamic-form
# or
yarn add @kbgarcia8/react-dynamic-form

Usage

Case 1: Using fieldsets prop

Supports multi-section forms that allows multiple groups of related inputs

This is designed for scenarios where your for needs to be split into multiple groups of input each representing different types of information

Example Scenario:

Cart Logic

  • Payment method information
  • Billing address
  • Shipping address
  • Contact details

Each of these becomes its own fieldset inside a single DynamicForm.

import { DynamicForm } from '@kbgarcia8/react-dynamic-form'
import type { FieldsetShape } from '@kbgarcia8/react-dynamic-form'

const addressInputsArray = [
    {
      //Input Props
      type: ... as const, id: ..., isRequired: ...,
      //dataAttributes is obtained through map
      disabled: ..., name: ..., value: ..., placeholderText: ...,
      //Start of Label Props
      textLabel: ..., additionalInfo: ..., $labelFlexDirection: ... as const, svg: <.../>,
      //Start of EditableInputProps
      labelClass: ..., inputClass: ..., isEditable: ...,
      //Additional in inputEntryShape
      editIcon: <.../>, //=>editIcon in EditableInputProps
      deleteIcon: <.../>,
      editing: ...,
      //These are informations editable within radio input acting as selection
      editableInformation: [
          {
              name: ...,
              info: ...,
              type: ... as const
          }
      ],
      //onClick functions obtained through map
    }
];

const fieldsets:FieldsetShape[] = [
	{
        legend: "Address Informations",
        inputs: addressInputsArray.map((address, index) => ({
            ...address,
            id: `${address.id}-${index}`,
            onChange: ...,
            onClickEdit: ...,
            onClickDelete: ...,
            onClickSave: ...,
            onClickCancel: ...,
            dataAttributes: {
                "data-index": index
            }
        })),
        isExpandable: true
    },
];

function App() {
  return (
    <DynamicForm
        className={'with-fieldsets'}
        fieldsets={fieldsets}
        id="address"
        isExpandable={true}
        {...props}
    />
  )
}

Case 2: Using formInputs prop

Supports single-section form that only needs one group of information

A legend text is optional in this part. This is a common/basic form scenario. It only needs an array of inputs which is rendered as a single form

Example Scenario:
  • Profile information
  • Login form
  • Address form
  • Single purpose editable section
import { DynamicForm } from '@kbgarcia8/react-dynamic-form'
import type { FieldsetShape } from '@kbgarcia8/react-dynamic-form'

const addressInputsArray = [
    {
      //Input Props
      type: ... as const, id: ..., isRequired: ...,
      //dataAttributes is obtained through map
      disabled: ..., name: ..., value: ..., placeholderText: ...,
      //Start of Label Props
      textLabel: ..., additionalInfo: ..., $labelFlexDirection: ... as const, svg: <.../>,
      //Start of EditableInputProps
      labelClass: ..., inputClass: ..., isEditable: ...,
      //Additional in inputEntryShape
      editIcon: <.../>, //=>editIcon in EditableInputProps
      deleteIcon: <.../>,
      editing: ...,
      //Styling options for editable option buttons
      editableButtonSize: 'small', //small by default. Value can be only be one of the following: "small" | "smallest" | "smaller" | "medium" | "large" | "larger"
      editableButtonColor: 'primary', //primary by default. Value can only be one of the following: "primary" | "secondary" | "ghost" | "bnw"
      editableButtonRadius: 'squircle', //squircle by default. Value can only be one of the following: "circle" | "square" | "roundedsquare" | "squircle" | "pill"
      //Optional props - text, start icon and end icon of editable option buttons
      saveText: ...,
      saveButtonStartIcon: <.../>,
      saveButtonEndIcon: <.../>,
      cancelText: ...,
      cancelButtonStartIcon: <.../>,
      cancelButtonEndIcon: <.../>,
      deleteText: ...,
      deleteButtonStartIcon: <.../>,
      deleteButtonEndIcon: <.../>,
      //These are informations editable within radio input acting as selection
      editableInformation: [
          {
              name: ...,
              info: ...,
              type: ... as const
          }
      ],
      //onClick functions obtained through map
    }
];

const addressInputs = addressInputsArray.map((address, index) => ({
    ...address,
    id: `${address.id}-${index}`,
    onChange: ...,
    onClickEdit: ...,
    onClickDelete: ...,
    onClickSave: ...,
    onClickCancel: ...,
    dataAttributes: {
        "data-index": index
    }
})),

function App() {
  return (
    <DynamicForm
        className={'with-fieldsets'}
        legendText={'Address Information'}
        formInputs={addressInputs}
        id="address"
        isExpandable={true}
        {...props}
    />
  )
}

API

Dynamic Form Props

| Prop | Type | Default | Description | | --------- | ------------ | -------- | ------------------------------- | | fieldsets | FieldsetShape[] | null | Used in multi-section form containing inputs divided into information groupsIf used, legendText and formInputs is no longer needed | | legendText | string | — | | | formInputs | inputEntryShape[] | — | If no fieldsets is provided, this is an object containing information for label and inputs of a single-section form | | isExpandable | boolean | false | If inputs are used as options commonly in type: checkbox/radio, this is to enabale a button for entry adding. Can also be used to add input type for details (e.g. text, email, etc.) If fieldset is null this is default to false | | id | string | — | Form tag id | | labelAndInputContainerClass | string | — | className for <LabeledInput/> component inside <DynamicForm/> | | labelClass | string | — | className for <Label/> component inside <LabeledInput/> component inside <DynamicForm/> | | inputClass | string | — | className for <Input/> component inside <LabeledInput/> component inside <DynamicForm/> | | handleEditableInputEntryChange | (e:React.ChangeEvent<HTMLInputElement\|HTMLTextAreaElement>) => void | — | Function to handle onChange of editable inputs | | handleAddingInputEntry | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to add input entry. If isExpandable is false this is not enabled | | formActionButtonSize | string | small | Text size of form action buttons. Values can only be one of the following values: "small", "smallest", "smaller", "medium", "large", "larger" | | formActionButtonColor | string | primary | Color of form action buttons. Values can only be one of the following: "primary", "secondary", "ghost", "bnw" | | formActionButtonRadius | string | squircle | Border radius of form action buttons. Values can only be one of the following: "circle", "square", "roundedsquare", "squircle", "pill" | | submitText | string | Submit | Text inside submit button for <DynamicForm/> | | handleSubmit | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle submit logic for <DynamicForm/> | | hasReset | boolean | false | This is to enable reset button for <DynamicForm/> | | resetText | string | Reset | Text inside reset button for <DynamicForm/> | | handleReset | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle reset logic for <DynamicForm/> | | hasCancel | boolean | false | This is to cancel submit button for <DynamicForm/> | | cancelText | string | Cancel | Text inside cancel button for <DynamicForm/> | | handleCancel | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle cancel logic for <DynamicForm/> | | handleSubmitForm | (e:React.FormEvent<HTMLFormElement>) => void | — | Function to handle form submit logic for <DynamicForm/> | | className | string | — | className for <DynamicForm/> | | children | React.ReactNode | — | React node/s or component placed after <FormActionButtons/> inside <DynamicForm/> |

Additional Props information

For editable input element of formInputs or inputs if fieldsets is used. Declared via inputEntryShape[] as NestedEditableOptionProps

| Prop | Type | Default | Description | | --------- | ------------ | -------- | ------------------------------- | | uniqueClass | string | — | className to uniquely select/distinguish a LabeledInput container | | isEditable | boolean | — | To determine if an input is editable or not. This is only applicable for radio or checkbox input to mimick an editable option/selectionIf false, all props below are automatically not needed | | editing | boolean | — | To identify if an editable input is being modifiedCan be used in open/close dialogs | | onClickEdit | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle edit logic of editable input | | editableButtonSize | string | small | Text size of editable option buttons. Values can only be one of the following values: "small", "smallest", "smaller", "medium", "large", "larger" | | editableButtonColor | string | primary | Color of editable option buttons. Values can only be one of the following: "primary", "secondary", "ghost", "bnw" | | editableButtonRadius | string | squircle | Border radius of editable option buttons. Values can only be one of the following: "circle", "square", "roundedsquare", "squircle", "pill" | | saveText | string | Save | Text inside save button for <NestedEditableOption/> | | saveButtonStartIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon before the text (if any) for save button of <NestedEditableOption/> | | saveButtonEndIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon after the text (if any) for save button of <NestedEditableOption/> | | cancelText | string | Cancel | Text inside cancel button for <NestedEditableOption/> | | cancelButtonStartIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon before the text (if any) for cancel button of <NestedEditableOption/> | | cancelButtonEndIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon after the text (if any) for cancel button of <NestedEditableOption/> | | deleteText | string | Delete | Text inside delete button for <NestedEditableOption/> | | deleteButtonStartIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon before the text (if any) for delete button of <NestedEditableOption/> | | deleteButtonEndIcon | React.ReactNode | — | TSX/JSX svg component that will serve as the icon after the text (if any) for delete button of <NestedEditableOption/> | | editIcon | React.ReactNode | — | TSX/JSX svg component that will serve as an icon for edit button of editable input | | onClickDelete | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle delete logic of editable input | | deleteIcon | React.ReactNode | — | TSX/JSX svg component that will serve as an icon for delete button of editable input | | onClickSave | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle save logic of editable input | | onClickCancel | (e:React.MouseEvent<HTMLButtonElement>) => void | — | Function to handle cancel logic of editable input |

For editableInformation object inside editable input. Declared as EditableInformation interface. Below is the properties needed:

| Property | Type | Description | | --------- | ------------ | ------------------------------- | | name | string | Serves as placeholder text for editable input | | info | string | Serves as value for editable input | | type | string | Input type of editable input |

For more information on props types see:

propTypes

Note that types/interfaces are also exported via npm package and can be imported as shown below:

import type { FieldsetShape, inputEntryShape } from '@kbgarcia8/react-dynamic-form'

Customization

Theme Support via styled components

Option 1: Users want theming via default Theme provided

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { ThemeContextProvider } from '@kbgarcia8/react-dynamic-form'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ThemeContextProvider>
      <App />
    </ThemeContextProvider>
  </StrictMode>,
)

Option 2: Users want custom theme

You allow overriding currentTheme:

<ThemeContextProvider initialTheme={myCustomLightTheme} secondTheme={myCustomDarkTheme}>
  <DynamicForm />
</ThemeContextProvider>
Reminder for custom theme override:

Below is the supported format for creating a theme object. Usually consisting of light and dark theme. Note that you can specify as many color key-color group you like. In the example below is 'colors', 'anchorTheme', 'footerTheme' and 'notificationPalette'

asColor function is used to ensure that color to be supplied in color properties are colors (e.g. hexcode and rgb code). Note that keys of colors are changeable since it has type ``

import { asColor } from '@kbgarcia8/react-dynamic-form'

export const palette = {
    primary1: asColor('#202234'),
    primary2: asColor('#3C5E83'),
    primary3: asColor('#0F60B6'),
    ...
}

export const lightTheme:Theme = {
    name: 'light',
    colors: {
      screenColor: palette.neutral2,
      backgroundColor1: palette.primary1,
      ...
    },
    anchorTheme: {
      visited: palette.neutral5,
      hover: palette.primary2,
      active: palette.secondary2
    },
    footerTheme: {
      backgroundColor: palette.neutral5,
      textColor: palette.secondary1,
      shadowColor: palette.shadow1
    },
    notificationPalette: {
      infoText: asColor('#C9E6F0'),
      infoBackground: asColor('#202234'),
    ...
    }
}

export const darkTheme:Theme = {
    name: 'dark',
    colors: {
      screenColor: palette.neutral5,
      backgroundColor1: palette.primary2,
      ...
    },
    anchorTheme: {
      link: palette.neutral1,
      visited: palette.neutral1,
      hover: palette.primary3,
      active: palette.secondary1
    },
    footerTheme: {
      backgroundColor: palette.accent,
      textColor: palette.primary1,
      shadowColor: palette.shadow2
    },
    notificationPalette: {
      infoText: asColor('#202234'),
      infoBackground: asColor('#C9E6F0'),
      ...
    }
}

Example usage of theme using styled-components

Inside <Component>.styles.[ts | js]

export const DefaultButton = styled.button`
  color: ${({theme})=> theme.colors.bg || 'white'};
`

Example usage of theme toggle inside component

import useTheme from '@kbgarcia8/react-dynamic-form'

const ThemeToggleButton = () => {
  const { currentTheme, toggleTheme } = useTheme();

  const isDark = currentTheme.name === 'dark' ? true : false;

  return (
    <Styled.Root
      checked={isDark}
      onCheckedChange={toggleTheme}
    >
      <Styled.Thumb $isDark={isDark}>
        {isDark ? <Moon size={20} /> : <Sun size={20} />}
      </Styled.Thumb>
    </Styled.Root>
  );
}

Styling components via styled-components

Available Default Class Names

Every renderable part of the form receives predictable classes or id: | Component | Default class name | Notes | | ---------------------------- | ----------------------------------------------------- | --------------------------- | | Form | ${id}-form | Root form container | | Fieldset Wrapper | ${id}-fieldset-wrapper | One per fieldset | | Fieldset | ${legend}-fieldset | Based on fieldset legend | | Legend | ${legend}-legend | Based on fieldset legend | | LabeledInput container | ${labelAndInputContainerClass} ${input.uniqueClass} | User-controlled | | Label | ${inputClass} | User-controlled (inside LabeledInput) | | Input | ${labelAndInputContainerClass} ${input.uniqueClass} | User-controlled (inside LabeledInput) | | Add Entry Button | add-input-entry | Used when isExpandable | | Add Entry Button Wrapper | add-input-button-space | | | No Entry Message | default styled component | Target using parent wrapper | | Children Container | children-container | |

Note/s

  • These classnames can also be used override styling using native CSS
import styled from "styled-components";
import { DynamicForm } from "@kbgarcia8/react-dynamic-form";

const StyledMyForm = styled(DynamicForm)`
  &.address-form {...}

  .address-fieldset-wrapper {...}

  .add-input-entry {...}

  .children-container {...}

  .<labelInputContainerClass> {...}

  .<inputClass> {...}

  .<labelClass> {...}
`;

Local Development

git clone https://github.com/kbgarcia8/react-dynamic-form
cd react-dynamic-form
npm install
npm run dev

Demo

git clone https://github.com/kbgarcia8/test-react-form
cd test-react-form
npm install
npm run dev

For the code of the demo where DynamicForm is used, please see: src/App.tsx

NOTE: This demo uses styled-components for styling. When using CSS you can provide class to components to style them accordingly

License

license