1.0.2 • Published 8 months ago

feather-form v1.0.2

Weekly downloads
-
License
MIT
Repository
github
Last release
8 months ago

Feather Form🪶

A lightweight and robust form component package with built-in validations for React applications. Feather form provides fully typed, accessible, and customizable form components with integrated validation support.

npm version

Features

  • 🎯 9 Pre-built form components
  • ✅ Built-in validation schemas
  • 🎨 Fully customizable with Tailwind CSS
  • 📱 Responsive and accessible
  • 🔒 TypeScript support
  • 🪶 Lightweight (~20KB gzipped)

Installation

npm install feather-form

or

yarn add feather-form

or

pnpm add feather-form

Basic Usage

import { Input, useFormValidation } from 'feather-form';
import 'feather-form/styles.css'

const MyForm = () => {
  const { values, errors, handleChange, handleBlur } = useFormValidation({
    username: ''
  });

  return (
    <Input
      name="username"
      label="Username"
      value={values.username}
      onChange={handleChange}
      onBlur={handleBlur}
      error={errors.username}
      schema="name"
    />
  );
};

Components

Input

A flexible input component for text based inputs like name, email and password with built-in validation.

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesInput label
valuestringYesInput value
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringNoValidation schema
stylePropsobjectNoStyle customization

Style Props

styleProps?: {
  container?: string;    // Wrapper div
  label?: string;        // Label element
  inputWrapper?: string; // Input container
  input?: string;        // Input element
  passwordToggle?: string; // Password visibility toggle
  errorText?: string;    // Error message
}

Example

<Input
  name="email"
  label="Email Address"
  value={values.email}
  onChange={handleChange}
  onBlur={handleBlur}
  error={errors.email}
  schema="email"
  styleProps={{
    container: "mb-4",
    label: "font-bold",
    input: "px-4 py-2"
  }}
/>

Select

A customizable select dropdown component

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesSelect label
valuestringYesSelected value
optionsstring[]YesValues to select from in dropdown
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringNoValidation schema
stylePropsobjectNoStyle customization

Style Props

styleProps?: {
  container?: string;        // Main container
  label?: string;         // Label element
  dropdownTrigger?: string; // Dropdown button
  dropdownList?: string;  // Options container
  option?: string;        // Individual option
  optionSelected?: string; // Selected option
  error?: string;         // Error message
}

Example

      <Select
        name="role"
        label="Job Role"
        error={errors.role}
        onBlur={handleBlur}
        onChange={handleChange}
        schema="select"
        value={values.role}
        options={["SDE1", "SDE2", "SDE3", "SA", "PE"]}
      />

Searchable Dropdown

A customizable searchable dropdown component

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesSelect label
valuestringYesSelected value
optionsstring[]YesValues to select from in dropdown
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringNoValidation schema
stylePropsobjectNoStyle customization

Style Props

styleProps?: {
  wrapper?: string;        // Main container
  label?: string;         // Label element
  dropdownTrigger?: string; // Dropdown button
  dropdownList?: string;  // Options container
  option?: string;        // Individual option
  optionSelected?: string; // Selected option
  error?: string;         // Error message
}

Example

      <SearchableDropdown
        name="department"
        label="Department"
        value={values.department}
        error={errors.department}
        onBlur={handleBlur}
        onChange={handleChange}
        options={[
          "Frontend",
          "Backend",
          "Database Management",
          "DevOps",
          "DevRel",
          "Mobile",
          "Human Resource",
        ]}
        schema="select"
      />

Radio Group

A flexible input component which allows you to render single-select radio buttons

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesGroup label
optionsstring[]YesRadio options
valuestringYesSelected value
onChangefunctionYesChange handler
errorstringNoError message

Style Props

styleProps?: {
  container?: string;       // Wrapper div
  label?: string;          // Group label
  optionsContainer?: string; // Container for radio options
  optionItem?: string;     // Individual radio item wrapper
  optionLabel?: string;    // Label for each radio option  
  radioInput?: string;     // Radio input element
  errorText?: string;      // Error message
}

Example

      <RadioGroup
        name="gender"
        label="Gender"
        options={["male", "female"]}
        value={values.gender}
        key="gender"
        onBlur={handleBlur}
        onChange={handleChange}
        error={errors.gender}
        schema="select"
      />

Text Area

A mulit-line text input useful for paragraph based content

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesTextArea label
valuestringYesInput value
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringNoValidation schema

Style Props

styleProps?: {
  container?: string;    // Wrapper div
  label?: string;       // Label element
  textArea?: string;    // Textarea element
  errorText?: string;   // Error message
}

Example

<TextArea
  name="coverLetter"
  label="Cover Letter"
  value={values.coverLetter}
  error={errors.coverLetter}
  schema="textarea"
  onBlur={handleBlur}
  onChange={handleChange}
  styleProps={{
    container: "mb-4",
    label: "font-bold",
    textArea: "p-2 h-32"
  }}
/>

Date Picker

A date picker component with built-in validation requiring date to be selected before submitting

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesTextArea label
valuestringYesInput value
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringNoValidation schema

Style Props

styleProps?: {
  container?: string;      // Wrapper div
  label?: string;         // Label element
  inputContainer?: string; // Date input container
  input?: string;         // Date input element
  errorText?: string;     // Error message
}

Example

<DatePicker
  name="dateOfBirth"
  label="Date of Birth"
  value={values.dateOfBirth}
  onChange={handleChange}
  onBlur={handleBlur}
  error={errors.dateOfBirth}
  styleProps={{
    container: "mb-4",
    label: "font-bold",
    input: "p-2"
  }}
/>

Time Picker

A time picker component with built-in validation requiring date to be selected before submitting

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesTime picker label
valuestringYesSelected time
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message

Style Props

styleProps?: {
  container?: string;      // Wrapper div
  label?: string;         // Label element
  inputContainer?: string; // Time input container
  input?: string;         // Time input element
  errorText?: string;     // Error message
}

Example

<TimePicker
  name="loginTime"
  label="Login Time"
  value={values.loginTime}
  onChange={handleChange}
  onBlur={handleBlur}
  error={errors.loginTime}
  styleProps={{
    container: "mb-4",
    label: "font-bold",
    input: "p-2"
  }}
/>

File Picker

A file picker component with built-in validation requiring the file to be selected before submitting

Props

PropTypeRequiredDescription
namestringYesField identifier
labelstringYesFile picker label
valueFileYesSelected file
onChangefunctionYesChange handler
onBlurfunctionYesBlur handler
errorstringNoError message
schemastringYesValidation schema

Style Props

styleProps?: {
  container?: string;    // Wrapper div
  label?: string;       // Label element
  input?: string;       // File input element
  button?: string;      // Upload button
  fileName?: string;    // Selected filename text
  errorText?: string;   // Error message
}

Example

<FilePicker
  name="resume"
  label="Resume"
  value={values.resume}
  onChange={handleChange}
  onBlur={handleBlur}
  error={errors.resume}
  schema="file"
  styleProps={{
    container: "mb-4",
    label: "font-bold",
    button: "bg-blue-500 text-white px-4 py-2 rounded"
  }}
/>

Submit Button

The submit button component of our useFormValidation component, it's responsible for submitting the form

Props

PropTypeRequiredDescription
disabledbooleanYesButton disabled state
labelstringNoButton text
stylePropsobjectNoStyle customization

Style Props

styleProps?: {
  base?: string;      // Base button styles
  enabled?: string;   // Enabled state styles
  disabled?: string;  // Disabled state styles
}

Example

<SubmitButton
  disabled={hasErrors()}
  label="Submit Form"
  styleProps={{
    base: "p-2 rounded text-white w-full",
    enabled: "bg-blue-500 hover:bg-blue-600",
    disabled: "bg-gray-400 cursor-not-allowed"
  }}
/>

Validation Schema

  • name: Validates names (letters only)
  • email: Validates email addresses
  • password: Minimum 8 characters, including letters, numbers, and special characters
  • select: Validates a value from the dropdown has been selected
  • radio: Validates one radio button is selected
  • textarea: Validates text area isn't empty
  • file: Validates file has been selected
  • date: Validates date selections

Complete Example

import React from "react";
import {
  DatePicker,
  FilePicker,
  Input,
  RadioGroup,
  SearchableDropdown,
  Select,
  SubmitButton,
  TextArea,
  TimePicker,
  useFormValidation
} from "feather-form";

import 'feather-form/styles.css'

const App = () => {
  const { values, errors, handleChange, handleBlur, hasErrors } =
    useFormValidation({
      name: "",
      email: "",
      password: "",
      coverLetter: "",
      gender: "",
      role: "",
      department: "",
      resume: "",
      dateOfBirth: "",
      loginTime: "",
    });

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    console.log("Form submitted:", values);
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-200 py-12 px-4 sm:px-6 lg:px-8">
      <div className="max-w-4xl mx-auto">
        <div className="text-center mb-12">
          <h1 className="text-4xl font-bold text-gray-900 mb-2">
            Feather From🪶
          </h1>
          <p className="text-gray-600">
            Elegant and Responsive Form Components
          </p>
        </div>

        <form
          className="bg-white rounded-2xl shadow-xl p-8 space-y-8"
          onSubmit={(e) => handleSubmit(e)}
        >
          {/* Personal Information Section */}
          <section className="space-y-6">
            <h2 className="text-xl font-semibold text-gray-800 pb-2 border-b">
              Personal Information
            </h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              <Input
                name="name"
                label="Full Name"
                value={values.name}
                error={errors.name}
                onChange={handleChange}
                onBlur={handleBlur}
                schema="name"
              />
              <Input
                name="email"
                label="Email Address"
                value={values.email}
                error={errors.email}
                onChange={handleChange}
                onBlur={handleBlur}
                schema="email"
              />
            </div>
            <Input
              name="password"
              label="Password"
              value={values.password}
              error={errors.password}
              onChange={handleChange}
              onBlur={handleBlur}
              schema="password"
            />
          </section>

          {/* Professional Details Section */}
          <section className="space-y-6">
            <h2 className="text-xl font-semibold text-gray-800 pb-2 border-b">
              Professional Details
            </h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              <Select
                name="department"
                label="Department"
                value={values.department}
                error={errors.department}
                onChange={handleChange}
                onBlur={handleBlur}
                options={["Engineering", "Design", "Marketing", "Sales"]}
                schema="select"
              />
              <SearchableDropdown
                name="role"
                label="Role"
                value={values.role}
                error={errors.role}
                onChange={handleChange}
                onBlur={handleBlur}
                options={["Developer", "Designer", "Manager", "Director"]}
                schema="select"
              />
            </div>
            <TextArea
              name="coverLetter"
              label="Cover Letter"
              value={values.coverLetter}
              error={errors.coverLetter}
              onChange={handleChange}
              onBlur={handleBlur}
              schema="textarea"
            />
          </section>

          {/* Additional Information Section */}
          <section className="space-y-6">
            <h2 className="text-xl font-semibold text-gray-800 pb-2 border-b">
              Additional Information
            </h2>
            <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              <DatePicker
                name="dateOfBirth"
                label="Date of Birth"
                value={values.dateOfBirth}
                error={errors.dateOfBirth}
                onChange={handleChange}
                onBlur={handleBlur}
              />
              <TimePicker
                name="loginTime"
                label="Preferred Login Time"
                value={values.loginTime}
                error={errors.loginTime}
                onChange={handleChange}
                onBlur={handleBlur}
              />
            </div>
            <RadioGroup
              name="gender"
              label="Gender"
              value={values.gender}
              error={errors.gender}
              onChange={handleChange}
              onBlur={handleBlur}
              options={["Male", "Female", "Other"]}
              schema="radio"
            />
            <FilePicker
              name="resume"
              label="Upload Resume"
              value={values.resume}
              error={errors.resume}
              onChange={handleChange}
              onBlur={handleBlur}
              schema="file"
            />
          </section>

          <div className="pt-6">
            <SubmitButton label="Submit Application" disabled={hasErrors()} />
          </div>
        </form>
      </div>
    </div>
  );
};

export default App;
1.0.2

8 months ago

1.0.1

8 months ago

1.0.0

8 months ago

0.0.1

8 months ago

0.0.0

8 months ago