Gathering detailed insights and metrics for @matthew.ngo/react-dynamic-form
Gathering detailed insights and metrics for @matthew.ngo/react-dynamic-form
Gathering detailed insights and metrics for @matthew.ngo/react-dynamic-form
Gathering detailed insights and metrics for @matthew.ngo/react-dynamic-form
A powerful and flexible React library for creating dynamic forms based on a configuration object.
npm install @matthew.ngo/react-dynamic-form
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (97.16%)
JavaScript (1.95%)
CSS (0.68%)
HTML (0.19%)
Shell (0.03%)
Verify real, reachable, and deliverable emails with instant MX records, SMTP checks, and disposable email detection.
Total Downloads
2,708
Last Day
3
Last Week
84
Last Month
741
Last Year
2,708
MIT License
182 Commits
1 Watchers
8 Branches
1 Contributors
Updated on Jan 13, 2025
Latest Version
0.0.37
Package Id
@matthew.ngo/react-dynamic-form@0.0.37
Unpacked Size
1.10 MB
Size
219.27 kB
File Count
110
NPM Version
10.5.0
Node Version
20.12.2
Published on
Feb 19, 2025
Cumulative downloads
Total Downloads
Last Day
-97.6%
3
Compared to previous day
Last Week
-45.8%
84
Compared to previous week
Last Month
-62.3%
741
Compared to previous month
Last Year
0%
2,708
Compared to previous year
6
4
55
@matthew.ngo/react-dynamic-form
is a powerful and flexible React library for creating dynamic forms based on a configuration object. It leverages react-hook-form
for form handling and yup
for validation, offering a streamlined and efficient way to build complex forms with minimal code. It also supports conditional fields, custom inputs, and theming with styled-components
.
View the live demo here: https://maemreyo.github.io/react-dynamic-form/
react-hook-form
Integration: Utilizes react-hook-form
for efficient form state management.yup
Validation: Supports schema-based validation using yup
.styled-components
.flex
and grid
layouts.onChange
: Provides a debounced onChange
callback.text
number
checkbox
select
textarea
email
password
tel
url
radio
date
switch
time
datetime-local
combobox
1yarn add @matthew.ngo/react-dynamic-form react-hook-form yup @hookform/resolvers styled-components
or
1npm install @matthew.ngo/react-dynamic-form react-hook-form yup @hookform/resolvers styled-components
1import React from 'react'; 2import { DynamicForm, DynamicFormProps } from '@matthew.ngo/react-dynamic-form'; 3 4const basicFormConfig: DynamicFormProps['config'] = { 5 firstName: { 6 type: 'text', 7 label: 'First Name', 8 defaultValue: 'John', 9 validation: { 10 required: { value: true, message: 'This field is required' }, 11 }, 12 }, 13 lastName: { 14 type: 'text', 15 label: 'Last Name', 16 defaultValue: 'Doe', 17 }, 18 email: { 19 type: 'email', 20 label: 'Email', 21 validation: { 22 required: { value: true, message: 'This field is required' }, 23 pattern: { 24 value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i, 25 message: 'Invalid email address', 26 }, 27 }, 28 }, 29}; 30 31const App: React.FC = () => { 32 const handleSubmit = (data: any) => { 33 console.log(data); 34 }; 35 36 return ( 37 <div> 38 <DynamicForm config={basicFormConfig} onSubmit={handleSubmit} /> 39 </div> 40 ); 41}; 42 43export default App;
1import React from 'react'; 2import { DynamicForm, DynamicFormProps } from '@matthew.ngo/react-dynamic-form'; 3import { FlexLayout } from '@matthew.ngo/react-dynamic-form'; 4 5const advancedFormConfig: DynamicFormProps['config'] = { 6 firstName: { 7 label: 'First Name', 8 type: 'text', 9 defaultValue: 'John', 10 validation: { 11 required: { value: true, message: 'This field is required' }, 12 }, 13 classNameConfig: { 14 input: 'border border-gray-400 p-2 rounded w-full', 15 label: 'block text-gray-700 text-sm font-bold mb-2', 16 }, 17 }, 18 subscribe: { 19 label: 'Subscribe to newsletter?', 20 type: 'checkbox', 21 defaultValue: true, 22 classNameConfig: { 23 checkboxInput: 'mr-2 leading-tight', 24 label: 'block text-gray-700 text-sm font-bold mb-2', 25 }, 26 }, 27 country: { 28 label: 'Country', 29 type: 'select', 30 defaultValue: 'US', 31 options: [ 32 { value: 'US', label: 'United States' }, 33 { value: 'CA', label: 'Canada' }, 34 { value: 'UK', label: 'United Kingdom' }, 35 ], 36 classNameConfig: { 37 select: 'border border-gray-400 p-2 rounded w-full', 38 label: 'block text-gray-700 text-sm font-bold mb-2', 39 }, 40 }, 41 gender: { 42 label: 'Gender', 43 type: 'radio', 44 defaultValue: 'male', 45 options: [ 46 { value: 'male', label: 'Male' }, 47 { value: 'female', label: 'Female' }, 48 { value: 'other', label: 'Other' }, 49 ], 50 classNameConfig: { 51 radioGroup: 'flex items-center', 52 radioLabel: 'mr-4', 53 radioButton: 'mr-1', 54 label: 'block text-gray-700 text-sm font-bold mb-2', 55 }, 56 }, 57 dynamicField: { 58 label: 'Dynamic Field', 59 type: 'text', 60 defaultValue: '', 61 conditional: { 62 when: 'firstName', 63 operator: 'is', 64 value: 'ShowDynamic', 65 fields: ['dynamicField'], 66 }, 67 classNameConfig: { 68 input: 'border border-gray-400 p-2 rounded w-full', 69 label: 'block text-gray-700 text-sm font-bold mb-2', 70 }, 71 }, 72 asyncEmail: { 73 label: 'Async Email Validation', 74 type: 'email', 75 validation: { 76 required: { value: true, message: 'This field is required' }, 77 validate: async (value: string): Promise<any> => { 78 // Simulate an async API call 79 const isValid = await new Promise<boolean>((resolve) => { 80 setTimeout(() => { 81 resolve(value !== 'test@example.com'); 82 }, 1000); 83 }); 84 return isValid || 'Email already exists (async check)'; 85 }, 86 }, 87 classNameConfig: { 88 input: 'border border-gray-400 p-2 rounded w-full', 89 label: 'block text-gray-700 text-sm font-bold mb-2', 90 errorMessage: 'text-red-500 text-xs italic', 91 }, 92 }, 93}; 94 95const App: React.FC = () => { 96 const handleSubmit = (data: any) => { 97 console.log(data); 98 alert(JSON.stringify(data)); 99 }; 100 101 return ( 102 <DynamicForm 103 config={advancedFormConfig} 104 renderLayout={({ children, ...rest }) => ( 105 <FlexLayout {...rest}>{children}</FlexLayout> 106 )} 107 formClassNameConfig={{ 108 formContainer: 'p-6 border border-gray-300 rounded-md', 109 inputWrapper: 'mb-4', 110 errorMessage: 'text-red-600', 111 button: 112 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded w-full', 113 }} 114 autoSave={{ 115 interval: 3000, 116 save: (data) => console.log('Auto-saving:', data), 117 }} 118 enableLocalStorage={true} 119 resetOnSubmit={true} 120 focusFirstError={true} 121 debounceOnChange={300} 122 onSubmit={handleSubmit} 123 onChange={(data) => console.log('Debounced change:', data)} 124 onFormReady={(form) => { 125 console.log('Form is ready:', form); 126 }} 127 showSubmitButton={true} 128 showInlineError={true} 129 showErrorSummary={true} 130 /> 131 ); 132}; 133 134export default App;
The DynamicForm
component accepts the following props:
Prop | Type | Default | Description |
---|---|---|---|
config | FormConfig | {} | The configuration object for the form. |
onChange | (formData: FormValues) => void | undefined | Callback function called when the form data changes (debounced if debounceOnChange is set). |
onSubmit | SubmitHandler<FieldValues> | undefined | Callback function called when the form is submitted. |
formOptions | UseFormProps | {} | Options for react-hook-form 's useForm hook. |
header | React.ReactNode | undefined | Header element for the form. |
footer | React.ReactNode | undefined | Footer element for the form. |
readOnly | boolean | false | Whether the form is read-only. |
disableForm | boolean | false | Whether the form is disabled. |
showSubmitButton | boolean | true | Whether to show the submit button. |
autoSave | { interval: number; save: (data: Record<string, any>) => void } | undefined | Auto-save configuration. |
resetOnSubmit | boolean | false | Whether to reset the form on submit. |
focusFirstError | boolean | false | Whether to focus on the first error field on submit. |
layout | 'flex' | 'grid' | 'grid' | The layout type for the form. |
layoutConfig | any | {} | Layout configuration. For grid , it can be { minWidth: '300px' } . For flex , it can be { gap: '10px' } . |
renderLayout | RenderLayoutProps | undefined | Custom layout renderer. |
horizontalLabel | boolean | false | Whether to use horizontal labels. |
labelWidth | string | number | undefined | Label width (for horizontal labels). |
enableLocalStorage | boolean | false | Whether to enable local storage for the form data. |
debounceOnChange | number | 0 | Debounce time (in ms) for the onChange callback. |
disableAutocomplete | boolean | false | Whether to disable autocomplete for the form. |
showInlineError | boolean | true | Whether to show inline error messages. |
showErrorSummary | boolean | false | Whether to show an error summary. |
validateOnBlur | boolean | false | Whether to validate on blur. |
validateOnChange | boolean | true | Whether to validate on change. |
validateOnSubmit | boolean | true | Whether to validate on submit. |
className | string | undefined | CSS class name for the form container. |
formClassNameConfig | FormClassNameConfig | {} | CSS class names for form elements. |
style | React.CSSProperties | undefined | Inline styles for the form container. |
theme | any | undefined | Theme object. You can provide custom theme. Please refer to ThemeProvider component for more information. |
onFormReady | (form: UseFormReturn<any>) => void | undefined | Callback function called when the form is ready. |
renderSubmitButton | (handleSubmit: (e?: React.BaseSyntheticEvent) => Promise<void>, isSubmitting: boolean) => React.ReactNode | undefined | Custom submit button renderer. |
renderFormContent | RenderFormContentProps | undefined | Custom form content renderer. |
renderFormFooter | RenderFormFooterProps | undefined | Custom form footer renderer. |
customValidators | { [key: string]: (value: any, context: any) => string | undefined } | undefined | Custom validators. |
customInputs | { [key: string]: React.ComponentType<CustomInputProps> } | undefined | Custom input components. |
onError | (errors: FieldErrors) => void | undefined | Error handler function. |
renderErrorSummary | (errors: FieldErrors, formClassNameConfig: FormClassNameConfig | undefined) => React.ReactNode | undefined | Custom error summary renderer. |
validationMessages | ValidationMessages | undefined | Custom validation messages. Use to override default validation messages. More detail at Validation section |
FormConfig
The FormConfig
object defines the structure and behavior of the form. Each key in the object represents a field in the form, and the value is a FieldConfig
object that defines the field's properties.
FieldConfig
Prop | Type | Default | Description |
---|---|---|---|
type | InputType | 'text' | The input type of the field. |
label | string | undefined | The label text for the field. |
placeholder | string | undefined | The placeholder text for the field. |
validation | ValidationConfig | undefined | The validation configuration for the field. |
component | React.ComponentType<any> | undefined | A custom component to use for rendering the field. |
style | React.CSSProperties | undefined | Inline styles for the input element. |
readOnly | boolean | false | Whether the field is read-only. |
clearable | boolean | false | Whether the field can be cleared. |
showCounter | boolean | false | Whether to show a character counter for the field (for text, textarea). |
copyToClipboard | boolean | false | Whether to enable copy-to-clipboard functionality for the field (for text, textarea). |
tooltip | string | undefined | Tooltip text for the field. |
classNameConfig | FieldClassNameConfig | undefined | CSS class names for the field's elements. |
options | { value: string; label: string }[] | undefined | Options for select, radio, or combobox inputs. |
conditional | Condition | undefined | Conditional logic for the field. |
fields | FormConfig | undefined | Nested fields (for complex inputs). |
validationMessages | { [key: string]: string | ((values: { label?: string; value: any; error: any; config: FieldConfig; }) => string) } | undefined | Custom validation messages for the field. Use to override default or global validation messages. Support function to provide dynamic message based on values. |
defaultValue | any | undefined | The default value for the field. |
InputType
Supported input types:
text
number
checkbox
select
textarea
email
password
tel
url
radio
date
switch
time
datetime-local
combobox
custom
ValidationConfig
Prop | Type | Default | Description |
---|---|---|---|
required | boolean | { value: boolean; message: string } | false | Whether the field is required. |
minLength | number | { value: number; message: string } | undefined | The minimum length of the field's value. |
maxLength | number | { value: number; message: string } | undefined | The maximum length of the field's value. |
min | number | string | { value: number | string; message: string } | undefined | The minimum value of the field (for number, date). |
max | number | string | { value: number | string; message: string } | undefined | The maximum value of the field (for number, date). |
pattern | RegExp | { value: RegExp; message: string } | undefined | A regular expression that the field's value must match. |
validate | (value: any, formValues: FormValues) => string | undefined | Promise<string | undefined> | undefined | A custom validation function. Returns an error message if validation fails, undefined otherwise. |
requiredMessage | string | undefined | Custom message for required validation. This prop is deprecated, use validationMessages instead |
Condition
Prop | Type | Default | Description |
---|---|---|---|
when | string | undefined | The field to watch for changes. |
operator | ComparisonOperator | 'is' | The comparison operator to use. |
value | any | undefined | The value to compare against. |
comparator | (value: any) => boolean | undefined | A custom comparator function (only used when operator is 'custom' ). |
fields | string[] | [] | The fields to show or hide based on the condition. |
ComparisonOperator
Supported comparison operators:
is
isNot
greaterThan
lessThan
greaterThanOrEqual
lessThanOrEqual
contains
startsWith
endsWith
custom
FormClassNameConfig
Prop | Type | Default | Description |
---|---|---|---|
formContainer | string | undefined | CSS class name for the form container. |
inputWrapper | string | undefined | CSS class name for the input wrapper. |
label | string | undefined | CSS class name for the label. |
input | string | undefined | CSS class name for the input element. |
errorMessage | string | undefined | CSS class name for the error message. |
button | string | undefined | CSS class name for the button. |
select | string | undefined | CSS class name for the select element. |
textarea | string | undefined | CSS class name for the textarea element. |
checkbox | string | undefined | CSS class name for the checkbox element. |
radio | string | undefined | CSS class name for the radio element. |
date | string | undefined | CSS class name for the date input element. |
number | string | undefined | CSS class name for the number input element. |
switch | string | undefined | CSS class name for the switch element. |
time | string | undefined | CSS class name for the time input element. |
dateTime | string | undefined | CSS class name for the datetime input element. |
comboBox | string | undefined | CSS class name for the combobox input element. |
radioGroup | string | undefined | CSS class name for the radio group. |
radioButton | string | undefined | CSS class name for the radio button. |
radioLabel | string | undefined | CSS class name for the radio label. |
checkboxInput | string | undefined | CSS class name for the checkbox input. |
switchContainer | string | undefined | CSS class name for the switch container. |
switchSlider | string | undefined | CSS class name for the switch slider. |
numberInputContainer | string | undefined | CSS class name for the number input container. |
numberInputButton | string | undefined | CSS class name for the number input button. |
comboBoxContainer | string | undefined | CSS class name for the combobox container. |
comboBoxDropdownList | string | undefined | CSS class name for the combobox dropdown list. |
comboBoxDropdownItem | string | undefined | CSS class name for the combobox dropdown item. |
FieldClassNameConfig
Same as FormClassNameConfig
, but applies to individual fields. FieldClassNameConfig
will override FormClassNameConfig
for that specific field.
Validation
You can define validation rules for each field in the validation
property of the FieldConfig
object. The validation
property can contain the following validation rules:
required
: Whether the field is required.minLength
: The minimum length of the field's value.maxLength
: The maximum length of the field's value.min
: The minimum value of the field (for number, date).max
: The maximum value of the field (for number, date).pattern
: A regular expression that the field's value must match.validate
: A custom validation function.You can provide custom validation messages for each field by using the validationMessages
property in the FieldConfig
object.
You can also provide global custom validation messages for the whole form by using the validationMessages
prop in the DynamicForm
component.
FieldConfig.validationMessages
will override DynamicForm.validationMessages
for that specific field.
1// Example usage 2const formConfig: DynamicFormProps['config'] = { 3 email: { 4 type: 'email', 5 label: 'Email', 6 validation: { 7 required: { value: true, message: 'This field is required' }, 8 pattern: { 9 value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i, 10 message: 'Invalid email address', 11 }, 12 }, 13 validationMessages: { 14 required: 'You must enter an email address.', // Override required message for this field 15 pattern: ({ value }) => `${value} is not a valid email address.`, // Dynamic message based on input value 16 }, 17 }, 18}; 19 20// Global validation messages 21const validationMessages: ValidationMessages = { 22 required: 'This is a globally defined required message', 23};
1interface ValidationMessages { 2 [key: string]: 3 | string // Static message 4 | ((values: { 5 label?: string; 6 value: any; 7 error: any; 8 config: FieldConfig; 9 }) => string); // Dynamic message function 10}
The validate
function receives two arguments:
value
: The current value of the field.formValues
: The current values of all fields in the form.The function should return a string if validation fails (the error message), or undefined
if validation passes. You can also return a Promise
that resolves to a string or undefined
for asynchronous validation.
1// Example custom validation function that checks if an email is already taken 2const validateEmailNotTaken = async (value: string) => { 3 const isTaken = await checkIfEmailExists(value); // Assume this function makes an API call 4 if (isTaken) { 5 return 'Email already taken'; 6 } 7 return undefined; 8}; 9 10// Example usage in a field configuration 11const formConfig: DynamicFormProps['config'] = { 12 email: { 13 type: 'email', 14 label: 'Email', 15 validation: { 16 validate: validateEmailNotTaken, 17 }, 18 }, 19};
You can create your own custom input components and use them in the form. To do this, pass a customInputs
prop to the DynamicForm
component. The customInputs
prop is an object where the keys are the input types and the values are the custom components.
1import React from 'react'; 2import { 3 DynamicForm, 4 DynamicFormProps, 5 CustomInputProps, 6} from '@matthew.ngo/react-dynamic-form'; 7 8// Example custom input component 9const MyCustomInput: React.FC<CustomInputProps> = ({ 10 fieldConfig, 11 formClassNameConfig, 12 field, 13 onChange, 14 value, 15}) => { 16 return ( 17 <div> 18 <label htmlFor={fieldConfig.id}>{fieldConfig.label}</label> 19 <input 20 id={fieldConfig.id} 21 type="text" 22 value={value || ''} 23 onChange={(e) => onChange(e.target.value)} 24 className={formClassNameConfig?.input} 25 /> 26 {/* You can add your own error handling here */} 27 </div> 28 ); 29}; 30 31// Example usage of custom input 32const myFormConfig: DynamicFormProps['config'] = { 33 customField: { 34 type: 'custom', 35 label: 'My Custom Field', 36 component: MyCustomInput, // Specify the custom component here 37 }, 38}; 39 40const App: React.FC = () => { 41 const handleSubmit = (data: any) => { 42 console.log(data); 43 }; 44 45 return ( 46 <DynamicForm 47 config={myFormConfig} 48 onSubmit={handleSubmit} 49 customInputs={{ 50 custom: MyCustomInput, // Register the custom input type 51 }} 52 /> 53 ); 54}; 55 56export default App;
You can customize the look and feel of the form by providing a custom theme object to the DynamicForm
component via the theme
prop. The theme
object should follow the styled-components
DefaultTheme
interface.
1import React from 'react'; 2import { 3 DynamicForm, 4 DynamicFormProps, 5 defaultTheme, 6} from '@matthew.ngo/react-dynamic-form'; 7import { ThemeProvider } from 'styled-components'; 8 9const myTheme = { 10 ...defaultTheme, 11 colors: { 12 ...defaultTheme.colors, 13 primary: 'blue', 14 }, 15}; 16 17const myFormConfig: DynamicFormProps['config'] = { 18 firstName: { 19 type: 'text', 20 label: 'First Name', 21 }, 22}; 23 24const App: React.FC = () => { 25 return ( 26 <ThemeProvider theme={myTheme}> 27 <DynamicForm config={myFormConfig} /> 28 </ThemeProvider> 29 ); 30}; 31 32export default App;
Contributions are welcome! Please read the contributing guidelines (TODO: create this file later) for details on how to contribute.
This project is licensed under the MIT License - see the LICENSE file for details.
No vulnerabilities found.
No security vulnerabilities found.