react-fatless-form
v1.2.1
Published
A lightweight React form package designed for simplicity that simplifies form handling and validation without unnecessary complexity or bloat.
Downloads
200
Maintainers
Keywords
Readme
react-fatless-form 🥬
A lightweight React form library designed for simplicity that simplifies form handling and validation without unnecessary complexity or bloat.
react-fatless-form
simplifies form management in React applications without leaving a huge footprint in your codebase. Whether you're building simple or complex forms, this package ensures a clean and intuitive experience with minimal setup.
Why fatless?
This package doesn't come with any baggage. It doesn't bring along a laundry list of dependencies, doesn't leave a huge footprint in your codebase, and doesn't force you to deal with unnecessary complexity. It doesn't try to do everything - it's a clean, simple solution that just works.
That’s why I went with the name react-fatless-form
. It’s my way of saying, “Hey, this is the lean form package you’ve been looking for.” No fluff, no bloated abstractions, and no over-engineered features you’ll never use. It’s light, sleek, and designed to keep your codebase as clean as possible while still delivering all the functionality you actually need.
Think of it as a form library that’s been on a diet :smile:. It’s still powerful, but it won’t weigh your project down. So if you’re looking for a form package that doesn't feel like dragging a sofa through your app, give react-fatless-form
a shot. You’ll feel the difference right away.
Features
- Lightweight: Minimal bundle size to keep your project fast.
- Developer-Oriented: Designed to make form handling straightforward for developers.
- Just Works: No unnecessary abstractions - integrate seamlessly into your workflow.
- Minimal Footprint: Clean and readable codebase integration.
- Validation Support: Built-in support for schema-based validation with Yup.
- Customizable: Easily adaptable to your specific form needs.
Design Philosophy
- Separation of Concerns: Modular handling of state, validation, and submission.
- Flexibility: Allows developers full control over the form lifecycle.
- Future-Proofing: Supports evolving workflows without tightly coupling form state with submission logic.
Installation
To get started, install react-fatless-form
and its peer dependency - yup.
npm install react-fatless-form yup
or using Yarn:
yarn add react-fatless-form yup
Provider
To use the package effectively, you need to wrap the relevant portion of your web app in the FormProvider
component. This ensures that the form state, validation, and submission logic are available via React's context API to all components within the form.
The FormProvider
component provides the react-fatless-form
context to its children, enabling them to access and interact with the form state and lifecycle. Without the FormProvider
, the components from react-fatless-form
will not work correctly, as they rely on the context for managing form data.
To get started, import FormProvider
and wrap your form in it. Pass the form
instance (from the useForm
hook) as a prop.
Hooks
useForm
The useForm
hook is a robust and developer-friendly solution for managing form state, validation, submission lifecycle, and user interactions in React applications. This hook is highly flexible and can adapt to a wide variety of use cases while maintaining a clean and intuitive API.
Features
- Form State Management: Tracks values, errors, and submission status.
- Validation Workflow: Integrates seamlessly with schema-based validation (e.g., yup).
- Submission Lifecycle: Provides functions to handle form submission and reset functionality.
API Documentation
Signature
export function useForm<T>(initialValues: T): {
values: T;
errors: Partial<Record<keyof T, string>>;
touched: Partial<Record<keyof T, boolean>>;
submissionStatus: "idle" | "submitting" | "success" | "error";
setFieldValue: (field: keyof T, value: T[keyof T]) => void;
setFieldArrayValue: (field: keyof T, value: string | string[]) => void;
setFieldError: (field: keyof T, error: string) => void;
setFieldTouched: (field: keyof T, touched: boolean) => void;
validate: (validateFn: (values: T) => Partial<Record<keyof T, string>>) => boolean;
resetForm: () => void;
updateSubmissionStatus: (status: "idle" | "submitting" | "success" | "error") => void;
resetSubmissionStatus: () => void;
};
Parameters
initialValues: T
Description: The initial state of the form’s values. Defines the default structure and data types of the form fields.
Type: T
(generic type representing the shape of the form values)
Return value
The useForm hook returns an object containing the following state, and functions:
State
1. values: T
The current state of the form’s values. Example:
{
username: "JohnDoe",
age: 25
}
2. errors: Partial<Record<keyof T, string>>
An object storing validation errors for each field. Example:
{
username: "Username is required"
}
3. touched: Partial<Record<keyof T, boolean>>
An object tracking whether a field has been interacted with. Example:
{
username: true
}
4. submissionStatus:
"idle" | "submitting" | "success" | "error"
The current status of the form submission. Possible values:
- "idle": No submission in progress.
- "submitting": Submission is in progress.
- "success": Submission completed successfully.
- "error": An error occurred during submission.
Functions
1. setFieldValue(field: keyof T, value: T[keyof T]) => void
Updates the value of a specific field. Example:
form.setFieldValue("username", "JaneDoe")
2. setFieldArrayValue(field: keyof T, value: string | string[]) => void
Sets the value of a field as a string or an array of strings. Example:
form.setFieldArrayValue("tags", ["React", "JavaScript"]);
3. setFieldError(field: keyof T, error: string) => void
Sets an error message for a specific field. Example
form.setFieldError("username", "Username is required");
4. setFieldTouched(field: keyof T, touched: boolean) => void
Marks a field as touched or untouched. Example:
form.setFieldTouched("username", true);
5. validate(validateFn: (values: T) => Partial<Record<keyof T, string>>) => boolean
Validates the form using a custom validation function. validateFn
receives the current form values and returns an object with field-specific error messages. Returns true
if validation passes (no errors), otherwise false
.
6. resetForm() => void
Resets the form’s values, errors, and touched fields to their initial state. Example:
form.resetForm();
7. updateSubmissionStatus(status: "idle" | "submitting" | "success" | "error") => void
Updates the submissionStatus to reflect the current state of submission. Example:
form.updateSubmissionStatus("submitting");
8. resetSubmissionStatus() => void
Resets the submissionStatus to "idle". Example:
form.resetSubmissionStatus()
:no_good_man: Leaving the form in a
"success"
or"error"
state can cause issues when usinguseForm
in multiple places. For example, submission-related logic tied to"idle"
won't execute if the form never returns to the"idle"
state. You must ensureresetSubmissionStatus
is called to reset the form's state. More on this later.
Example usage
:love_you_gesture: This example will be used throughout the remaining documentation, undergoing progressive refinements.
import { FormProvider, useForm } from "react-fatless-form";
function MyForm() {
const {
validate,
values,
errors,
updateSubmissionStatus,
resetForm,
resetSubmissionStatus,
setFieldValue,
setFieldTouched,
} = useForm({ username: "", age: 0 });
const validateFn = values => {
const errors = {};
if (!values.username) errors.username = "Username is required";
if (values.age <= 0) errors.age = "Age must be positive";
return errors;
};
const onSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();
if (!validate(() => validateFn(values))) {
console.warn("Validation failed");
return;
}
updateSubmissionStatus("submitting");
try {
await apiCall(values);
updateSubmissionStatus("success");
resetForm();
} catch (error) {
console.error("Error during submission:", error);
updateSubmissionStatus("error");
} finally {
resetSubmissionStatus();
}
};
return (
<FormProvider form={form}>
<form onSubmit={onSubmit}>
<input
value={values.username}
onChange={(e) => setFieldValue("username", e.target.value)}
onBlur={() => setFieldTouched("username", true)}
/>
{errors.username && <span>{errors.username}</span>}
<input
type="number"
value={values.age}
onChange={(e) => setFieldValue("age", parseInt(e.target.value, 10))}
onBlur={() => setFieldTouched("age", true)}
/>
{errors.age && <span>{errors.age}</span>}
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
Utilities
FeedbackManager
The FeedbackManager class is a centralized utility for managing feedback notifications - specifically toasts and alerts, with features like auto-dismissal, customizable durations, and fade-out animations. It follows a subscription-based model, making it easy to integrate with UI components for real-time feedback updates.
Features
- Supports Feedback Types: "toast" and "alert".
- Visual Variants: "info", "success", "error", and "warning".
- Auto-Dismiss Functionality: Configurable durations for automatic dismissal of feedback.
- Fade-Out Animations: Handles graceful removal with fade-out effects.
- Subscription Model: Provides real-time updates to registered listeners.
- UI Integration: Works seamlessly with the FeedbackContainer component for rendering feedback notifications.
API Documentation
Types
type FeedbackVariant = "info" | "success" | "error" | "warning";
// Represents a single feedback notification.
interface Feedback {
id: number;
message: string;
type: "toast" | "alert";
variant: FeedbackVariant;
autoDismiss?: boolean;
duration?: number;
onClose?: () => void;
isFadingOut: boolean;
}
// Optional configuration for the addFeedback method.
interface FeedbackOptions = {
type?: "toast" | "alert"; // Type of feedback (default: "toast")
variant?: FeedbackVariant; // Visual variant (default: "info")
autoDismiss?: boolean; // Whether the feedback should dismiss automatically (default: true)
duration?: number; // Duration in milliseconds for auto-dismissal (default: 5000ms)
onClose?: () => void; // Callback executed when the feedback is removed
}
Methods
1. addFeedback(message: string, options?: FeedbackOptions): void
Adds a new feedback notification to the list.
Parameters
message: string
- The feedback message to display.options: FeedbackOptions
- Optional configuration object
2. removeFeedback(id: number): void
Removes feedback immediately and triggers its onClose callback, if provided.
Parameters
id: number
: Unique identifier of the feedback to be removed.
3. subscribe(listener: (feedbacks: Feedback[]) => void): () => void
Registers a listener for real-time feedback updates.
Parameters
listener: (feedbacks: Feedback[]) => void
: Callback function invoked with the current list of feedbacks.
Returns
A function to unsubscribe the listener.
Internal Methods
1. startFadeOut(id: number): void
Initiates the fade-out animation for a feedback notification before removing it.
2. notifyListeners(): void
Notifies all registered listeners of feedback updates.
Usage
Mounting the Feedback Container
The FeedbackContainer
component listens for updates and renders feedback notifications appropriately. Add it once to your application, typically in your app's root component.
import { FeedbackContainer } from 'react-fatless-form';
function App() {
return (
<div>
<YourMainContent />
<FeedbackContainer />
</div>
);
}
The component uses ReactDOM.createPortal
to render notifications at the root of document.body
.
Importing and Instantiating
import { feedbackManager } from 'react-fatless-form';
Adding Feedback
feedbackManager.addFeedback("Operation successful!", {
type: "toast",
variant: "success",
autoDismiss: true,
duration: 5000,
onClose: () => console.log("Feedback closed!"),
});
Subscribing to Feedback Updates
const unsubscribe = feedbackManager.subscribe(feedbacks => {
console.log("Current feedbacks:", feedbacks);
});
// Unsubscribe when no longer needed
unsubscribe();
Example Integration with UI
import { FeedbackContainer, feedbackManager } from 'react-fatless-form';
const App = () => {
const handleClick = () => {
feedbackManager.addFeedback("This is a success message!", {
type: "toast",
variant: "success",
duration: 3000,
});
};
return (
<div>
<button onClick={handleClick}>Show Feedback</button>
<FeedbackContainer />
</div>
);
};
Example usage
import { FormProvider, useForm, feedbackManager } from "react-fatless-form";
function MyForm() {
const {
validate,
values,
errors,
updateSubmissionStatus,
resetForm,
resetSubmissionStatus,
setFieldValue,
setFieldTouched,
} = useForm({ username: "", age: 0 });
const validateFn = values => {
const errors = {};
if (!values.username) errors.username = "Username is required";
if (values.age <= 0) errors.age = "Age must be positive";
return errors;
};
const onSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();
if (!validate(() => validateFn(values))) {
console.warn("Validation failed");
return;
}
updateSubmissionStatus("submitting");
try {
await apiCall(values);
updateSubmissionStatus("success");
feedbackManager.addFeedback("Submission successful!", {
type: "toast",
variant: "success",
autoDismiss: true,
duration: 5000,
onClose: () => console.log("Feedback closed!"),
});
resetForm();
} catch (error) {
console.error("Error during submission:", error);
updateSubmissionStatus("error");
feedbackManager.addFeedback("That didn't go well!", {
type: "toast",
variant: "error",
autoDismiss: true,
duration: 5000,
onClose: () => console.log("Feedback closed!"),
});
} finally {
resetSubmissionStatus();
}
};
return (
<FormProvider form={form}>
<form onSubmit={onSubmit}>
<input
name="username"
value={values.username}
onChange={(e) => setFieldValue("username", e.target.value)}
onBlur={() => setFieldTouched("username", true)}
/>
{errors.username && <span>{errors.username}</span>}
<input
name="age"
type="number"
value={values.age}
onChange={(e) => setFieldValue("age", parseInt(e.target.value, 10))}
onBlur={() => setFieldTouched("age", true)}
/>
{errors.age && <span>{errors.age}</span>}
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
Notes
- The
FeedbackManager
is a singleton instance provided by thereact-fatless-form
. - To display feedback notifications, ensure the
FeedbackContainer
component is mounted in your application - typically in your app's root component. - Fade-out animations provide a smooth user experience and are automatically handled before feedback removal.
validateSchema
The validateSchema
utility function is a simple and efficient tool for validating form values against a schema defined using the yup validation library. It provides a structured way to collect validation errors, making it easy to integrate with form handling workflows.
API Documentation
Signature
function validateSchema<T extends Record<string, any>>(
schema: yup.ObjectSchema<T>,
values: T
): Partial<Record<keyof T, string>>
Parameters
1. schema: yup.ObjectSchema<T>
The validation schema defining the rules for the form fields. This is a yup object schema tailored to the structure of the values being validated.
2. values: T
The object containing the form field values to be validated against the schema.
Return value
An object containing validation errors. Each key represents the name of an invalid field, and its value is the corresponding error message. If no validation errors are found, an empty object {}
is returned.
Behavior
Validation Process
- The
validateSchema
function uses theschema.validateSync()
method from yup to perform validation. - The
abortEarly: false
option ensures all errors are collected, not just the first one.
Error Handling
- If the validation fails, the errors are collected from the inner property of the
yup.ValidationError
object. - The errors are returned as a flat object, where each field’s name is mapped to its corresponding error message.
Example usage
import * as yup from "yup";
import { FormProvider, useForm, feedbackManager, validateSchema } from "react-fatless-form";
// Define a schema
const schema = yup.object({
username: yup.string().required("Name is required"),
age: yup.number().min(18, "Must be at least 18").required("Age is required"),
});
function MyForm() {
const {
validate,
values,
errors,
updateSubmissionStatus,
resetForm,
resetSubmissionStatus,
setFieldValue,
setFieldTouched,
} = useForm({ username: "", age: 0 });
const onSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();
if (!validate(() => validateSchema(schema, values))) {
console.warn("Validation failed");
return;
}
updateSubmissionStatus("submitting");
try {
await apiCall(values);
updateSubmissionStatus("success");
feedbackManager.addFeedback("Submission successful!", {
type: "toast",
variant: "success",
autoDismiss: true,
duration: 5000,
onClose: () => console.log("Feedback closed!"),
});
resetForm();
} catch (error) {
console.error("Error during submission:", error);
updateSubmissionStatus("error");
feedbackManager.addFeedback("That didn't go well!", {
type: "toast",
variant: "error",
autoDismiss: true,
duration: 5000,
onClose: () => console.log("Feedback closed!"),
});
} finally {
resetSubmissionStatus();
}
};
return (
<FormProvider form={form}>
<form onSubmit={onSubmit}>
<input
name="username"
value={values.username}
onChange={(e) => setFieldValue("username", e.target.value)}
onBlur={() => setFieldTouched("username", true)}
/>
{errors.username && <span>{errors.username}</span>}
<input
name: "age"
type="number"
value={values.age}
onChange={(e) => setFieldValue("age", parseInt(e.target.value, 10))}
onBlur={() => setFieldTouched("age", true)}
/>
{errors.age && <span>{errors.age}</span>}
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
handleSubmit
The handleSubmit
utility simplifies form submissions in React applications by integrating schema-based validation, submission status management, and optional feedback notifications. Designed to work seamlessly with the useForm
hook, it reduces boilerplate code and enforces best practices for managing the form lifecycle.
Features
- Schema-Based Validation: Ensures form data adheres to a defined structure using yup.
- Submission Status Updates: Automatically updates form status ("submitting", "success", "error") for improved user feedback and state management.
- Feedback Notifications: Optional toast notifications for submission success or error states.
- Flexible Feedback Control: Allows developers to enable or disable default feedback handling for custom solutions.
- Promise-Based API: Fully compatible with async/await for smooth integration.
API Documentation
Signature
function handleSubmit<T extends Record<string, any>>(
form: ReturnType<typeof useForm<T>>,
schema: yup.ObjectSchema<T>,
onSubmit: (values: T) => Promise<void>,
successMessage?: string,
showFeedback?: boolean
): Promise<void>
Parameters
form: ReturnType<typeof useForm<T>>
- The form object returned by theuseForm
hook.schema: yup.ObjectSchema<T>
- A yup schema defining the structure and constraints of form values.onSubmit: (values: T) => Promise<void>
- An async callback for form submission logic. Receives validated form values as an argument.successMessage?: string
- A success message displayed upon successful submission. Defaults to "Done!".showFeedback?: boolean
- Controls whether feedback notifications are displayed. Defaults totrue
.
Returns
Promise<void>
- Resolves when the submission process is complete.
Usage
Schema Definition with yup
import * as yup from "yup";
import { useForm, feedbackManager, validateSchema } from "react-fatless-form";
// Define a schema
const schema = yup.object({
username: yup.string().required("Name is required"),
age: yup.number().min(18, "Must be at least 18").required("Age is required"),
});
Basic Integration
import { useForm, handleSubmit } from 'react-fatless-form';
const form = useForm({ username: "", age: 0 });
const handleFormSubmit = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
await handleSubmit(
form,
schema,
async (values) => {
const result = await api.submitData(values);
if (!result.ok) throw result; // Ensure errors are thrown for handleSubmit to catch
},
"Submission successful!"
);
};
Disabling Feedback
import { useForm, feedbackManager } from 'react-fatless-form';
const form = useForm({ username: "", age: 0 });
const handleFormSubmit = async (e: React.FormEvent<HTMLFormElement>): Promise<void> => {
e.preventDefault();
await handleSubmit(
form,
schema,
async (values) => {
const result = await api.submitData(values);
if (!result.ok) {
throw result; // Ensure errors are thrown for handleSubmit to catch
}
},
"Submission successful!",
false // Disable default feedback
);
// Custom feedback handling
feedbackManager.addFeedback("Submission successful!", {
type: "toast",
variant: "success",
autoDismiss: true,
duration: 5000,
onClose: () => form.resetSubmissionStatus(), // Reset form submission status to "idle"
});
};
Best Practices
Resetting Submission Status
When feedback is disabled, ensure that the form’s submission status is reset to "idle" after a submission. This avoids issues with multiple uses of useForm
.
form.resetSubmissionStatus();
Custom Feedback
Leverage feedbackManager
or your own UI for personalized user feedback. The flexibility of disabling showFeedback
empowers developers to craft unique experiences while adhering to state management requirements.
Example usage
import * as yup from "yup";
import { FormProvider, useForm, handleSubmit } from "react-fatless-form";
// Define a schema
const schema = yup.object({
username: yup.string().required("Name is required"),
age: yup.number().min(18, "Must be at least 18").required("Age is required"),
});
function MyForm() {
const form = useForm({ username: "", age: 0 });
const onSubmit = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
await handleSubmit(
form,
schema,
async (values) => {
const result = await api.submitData(values);
if (!result.ok) throw result; // Ensure errors are thrown for handleSubmit to catch
},
"Submission successful!"
);
};
return (
<FormProvider form={form}>
<form onSubmit={onSubmit}>
<input
name="username"
value={form.values.username}
onChange={(e) => setFieldValue("username", e.target.value)}
onBlur={() => setFieldTouched("username", true)}
/>
{form.errors.username && <span>{form.errors.username}</span>}
<input
name="age"
type="number"
value={form.values.age}
onChange={(e) => setFieldValue("age", parseInt(e.target.value, 10))}
onBlur={() => setFieldTouched("age", true)}
/>
{form.errors.age && <span>{form.errors.age}</span>}
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
Why Choose handleSubmit
?
- Reduced Boilerplate: Automates validation, submission status updates, and feedback management.
- Flexibility: Customize feedback or rely on built-in options.
- Best Practices: Encourages clean, predictable form handling with clear state transitions.
Components
Input
:nerd_face:
The Input
component is a versatile and self-sufficient form control designed to handle a wide variety of input scenarios. It dynamically adapts its behavior based on the type prop and comes with built-in features to simplify form management.
Features
1. Dynamic Type Handling
Renders different input types (text, number, password, email, etc.) based on the type prop.
2. Custom Datepicker
- Includes a fully custom datepicker component, eliminating the need for external libraries.
- Provides an intuitive UI for date selection.
3. Custom Drag-and-Drop file picker
- Features a built-in drag-and-drop interface for file uploads.
- Supports multiple file uploads
- Displays selected file names, and has the feature to select and remove
4. Integrated Form State Management
- Automatically binds to form fields, handling value and onChange props.
- Manages field state and validation seamlessly.
5. Developer-Friendly
- Fully self-sufficient and requires no external dependencies for advanced features like datepickers or drag-and-drop file uploads.
- Provides a simple API, allowing developers to focus on configuration without worrying about state management or third-party library integration.
6. Customizable and Themed
- Supports custom styles via className and style props.
- Adapts to form-level and global styling conventions.
7. Type-Safe Props
Each input type enforces its own specific props, ensuring valid usage.
Supported Input Types
- Text-based Inputs: Includes
text
,number
,password
, etc. - Textarea: Multi-line text input with options for rows, columns, and wrapping.
- Checkbox: Supports both standalone checkboxes and grouped checkboxes.
- Radio Buttons: Renders a group of mutually exclusive options.
- Select Dropdown: A dropdown menu with options for single or multiple selection.
- Date Picker: Renders a date input with optional minimum and maximum date constraints.
- File Input: For uploading files, with support for specifying file types and allowing multiple file uploads.
Common Props
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| name
| string
(required) | The name of the input field, used for form state binding. |
| label
| string
(required) | The label text displayed for the input field. |
| disabled
| boolean
(optional) | Disables the input field if true. |
| required
| boolean
(optional) | Marks the input field as required. |
| className
| string
(optional) |Adds custom CSS classes to the input field for styling.|
| style
| React.CSSProperties
(optional) | Adds inline styles for the input field. |
Type-Specific Props
Text Inputs (type: "text" | "number" | "password")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| placeholder
| string
| Placeholder text for the input. |
| autofocus
| boolean
| Automatically focuses the input field on mount. |
Textarea (type: "textarea")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| cols
| number
| Number of columns for the textarea. |
| rows
| number
| Number of rows for the textarea. |
| wrap
| string
| "hard" or "soft" - Specifies how the text in a text area is to be wrapped when submitted in a form|
| readonly
| boolean
| Prevents modification of the text if true. |
| maxlength
| number
| Maximum number of characters allowed. |
Checkbox (type: "checkbox")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| checked
| boolean
| Indicates if the checkbox is selected. |
| options
| { label: string; value: any }[]
| Array of checkbox options for grouped checkboxes. |
| slider
| string
| "rounded" or "default" - If provided, renders a single checkbox as a switch. "default" renders a rounded switch. "rounded" renders a rounded switch. |
Behavior
:point_right: Single Checkbox
- If
options
is not provided, it renders a single checkbox. - If
slider
is provided, the checkbox is styled as a switch.
:point_right: Multiple Checkboxes
- If
options
is provided and has at least one item, it renders a list of checkboxes. - If
options
is empty, the component renders nothing.
Radio Buttons (type: "radio")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| checked
| boolean
| Indicates if the checkbox is selected. |
| options
| { label: string; value: any }[]
| Array of radio button options. |
Select Dropdown (type: "select")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| options
| { label: string; value: any }[]
| Array of dropdown options. |
| loading
| boolean
| Displays a loading indicator if true. |
| multiple
| boolean
| Enables multi-selection if true
. |
| placeholder
| string
| Placeholder text for the dropdown. |
Date Input (type: "date")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| minDate
| Date
| Minimum selectable date. |
| maxDate
| Date
| Maximum selectable date. |
| placeholder
| string
| Placeholder text for the date input. |
File Input (type: "file")
| Prop | Type | Description |
|------------------|----------------------------------------|--------------------------------------------------------------------------------------------------|
| accept
| string
| Accepted file types (e.g., .pdf, .docx). |
| multiple
| boolean
| Allows selection of multiple files if true. |
Examples
1. Text Input
<Input name="username" type="text" label="Username" placeholder="Enter your username" />
2. Single Checkbox (Default)
<Input
name="acceptTerms"
type="checkbox"
label="Accept Terms and Conditions"
/>
3. Single Checkbox (Slider)
<Input
name="darkMode"
type="checkbox"
label="Enable Dark Mode"
slider="rounded"
/>
4. Multiple Checkboxes
<Input
name="preferences"
type="checkbox"
label="Choose Preferences"
options={[
{ label: 'Option 1', value: 'option1' },
{ label: 'Option 2', value: 'option2' },
]}
/>
5. Date Input
<Input
name="preferredDate"
type="date"
minDate={new Date()} // Restrict to no past dates
maxDate={new Date(2025, 11, 31)} // Allow dates only up to Dec 31, 2025
className="custom-date-input" // Add custom styling to the input field
/>
6. File Input
<Input name="files" type="file" label="Relevant Files" accept=".pdf,.docx" multiple />
Full Usage Example :sparkles:
import * as yup from "yup";
import { FormProvider, useForm, handleSubmit, Input } from "react-fatless-form";
// Define a schema
const schema = yup.object({
username: yup
.string()
.required("Username is required"),
age: yup
.number()
.typeError("Age must be a number")
.min(18, "Must be at least 18")
.required("Age is required"),
dateAvailable: yup
.date()
.typeError("Must be a valid date")
.required("Availability date is required"),
relevantFiles: yup
.array()
.required("This field is required")
.min(1, "At least one file must be availed")
.test("fileType", "Invalid file type", (files) => {
if (!files || files.length === 0) return true;
return files
.every((file) => [
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/msword"
]
.includes(file.type));
})
.test("fileSize", "File is too large", (files) => {
if (!files || files.length === 0) return true;
return files
.every((file) => file.size <= 2 * 1024 * 1024); // Max 2MB
}),
preferredCountriesOfWork: yup
.array()
.of(yup
.string()
.typeError("Each item must be a string")
.required("Country is required")
)
.min(1, "At least one country must be selected")
.required("This field is required")
});
function MyForm() {
const form = useForm({
username: "",
age: 18,
dateAvailable: new Date(),
relevantFiles: [],
preferredCountriesOfWork: [],
});
const onSubmit = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
event.preventDefault();
await handleSubmit(
form,
schema,
async (values) => {
console.log(values);
},
"Submission successful!"
);
};
return (
<>
<FormProvider form={form}>
<form onSubmit={onSubmit}>
<Input name="username" type="text" label="Username" placeholder="Your username" />
<Input name="age" type="number" label="Age" placeholder="Your age" />
<Input name="dateAvailable" type="date" label="Date Available" />
<Input name="preferredCountriesOfWork" type="select" label="Preferred Countries" options={[
{label: "Kenya", value: "ke"},
{label: "Ethiopia", value: "et"},
{label: "Nigeria", value: "ng"},
{label: "South Africa", value: "sa"}
]} placeholder="Select countries" multiple />
<Input name="relevantFiles" type="file" label="Relevant files" accept=".doc,.docx" multiple />
<button type="submit">Submit</button>
</form>
</FormProvider>
<FeedbackContainer />
</>
);
}
Tech
react-fatless-form
uses two open source projects to work properly:
- React - The library for web and native user interfaces
- React DOM - Serves as the entry point to the DOM and server renderers for React
- Yup - A schema builder for runtime value parsing and validation.
License
MIT
I'm super chill about how you use this software, basically letting you do whatever you want with it, even for commercial purposes – it's the definition of open source freedom! Just be sure to include the provided license.
Adera Henry, with :heart: from Nairobi, Kenya