Dynamic Form Wrapper
In most apps you should not sprinkle NvsDynamicForm configuration across many pages.
Instead, create a project-level DynamicForm component that pre-configures:
formElementsregistry (field type → renderer + model class)buttonComponent- default submit button options
Then your pages only pass fields and onSubmit.
This page also includes minimal Input and Textarea field implementations.
1) Create field model classes + renderers
Input
components/dynamic-form/fields/input.tsx
import React, { type ChangeEvent } from 'react';
import { FieldBase } from '@nvs-dynamic-form/react-core';
export class InputField extends FieldBase<string> {
override readonly fieldType? = 'input';
placeholder?: string;
type?: 'email' | 'number' | 'password' | 'tel' | 'text' | 'url';
constructor(options: InputField) {
super(options, '');
this.placeholder = options.placeholder ?? '';
this.type = options.type ?? 'text';
}
}
export function InputElement(props: any) {
return (
<input
{...props}
style={{ width: '100%', boxSizing: 'border-box' }}
placeholder={props.placeholder}
type={props.type ?? 'text'}
onChange={(e: ChangeEvent<HTMLInputElement>) => {
props.onChange?.(e);
props.field?.onChange?.(e);
}}
/>
);
}
Textarea
components/dynamic-form/fields/textarea.tsx
import React from 'react';
import { FieldBase } from '@nvs-dynamic-form/react-core';
export class TextareaField extends FieldBase<string> {
override readonly fieldType? = 'textarea';
placeholder?: string;
minRowSize?: number;
maxRowSize?: number;
constructor(options: TextareaField) {
super(options, '');
this.placeholder = options.placeholder ?? '';
this.minRowSize = options.minRowSize ?? 3;
this.maxRowSize = options.maxRowSize ?? 10;
}
}
export function TextareaElement(props: any) {
return (
<textarea
{...props}
style={{ width: '100%', boxSizing: 'border-box' }}
rows={props.minRowSize ?? 3}
placeholder={props.placeholder}
/>
);
}
2) Create the wrapper component
components/dynamic-form/DynamicForm.tsx
import React from 'react';
import {
NvsDynamicForm,
type INvsDynamicForm,
} from '@nvs-dynamic-form/react-core';
import { InputElement, InputField } from './fields/input';
import { TextareaElement, TextareaField } from './fields/textarea';
import { Button } from './ui/Button';
export type DynamicFormProps = Pick<
INvsDynamicForm,
| 'fields'
| 'onSubmit'
| 'formClass'
| 'submitButtonVisible'
| 'submitButtonLabel'
| 'submitButtonIsFullWidth'
| 'submitButtonPosition'
| 'doubleSubmitProtection'
>;
export function DynamicForm(props: DynamicFormProps) {
return (
<NvsDynamicForm
{...props}
buttonComponent={Button}
submitButtonDefaultOptions={{
label: 'Save',
isFullWidth: true,
position: 'right',
}}
formElements={{
input: {
component: InputElement,
class: InputField,
},
textarea: {
component: TextareaElement,
class: TextareaField,
},
}}
/>
);
}
3) Use it in a page
pages/ProfileForm.tsx
import React from 'react';
import * as Yup from 'yup';
import { DynamicForm } from '../components/dynamic-form/DynamicForm';
import { InputField } from '../components/dynamic-form/fields/input';
import { TextareaField } from '../components/dynamic-form/fields/textarea';
export function ProfileFormPage() {
const fields = [
new InputField({
id: 'firstName',
label: 'First name',
placeholder: 'Jane',
validate: Yup.string().required(),
screenSize: { desktop: 6, mobile: 12 },
}),
new InputField({
id: 'lastName',
label: 'Last name',
placeholder: 'Doe',
screenSize: { desktop: 6, mobile: 12 },
}),
new TextareaField({
id: 'about',
label: 'About',
placeholder: 'Tell us about yourself…',
minRowSize: 6,
screenSize: 12,
}),
];
return (
<DynamicForm
fields={fields}
onSubmit={(values) => {
console.log(values);
}}
/>
);
}