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

hazo_data_forms

v1.0.7

Published

Dynamic form rendering from JSON schema with document link support and PDF viewer integration

Readme

hazo_data_forms

Dynamic form rendering from JSON schema with document link support and embedded PDF viewer integration.

Overview

hazo_data_forms is a React library that transforms JSON schema definitions into fully functional forms with rich field types, dual-mode rendering (edit/view), and integrated PDF document viewing capabilities. Build complex data collection interfaces without writing form components.

Key Features

  • 17 Field Types: text, number, date, boolean, option, email, tel, currency, percentage, textarea, table, computed, masked, static_text, summary_row, abn, tfn
  • Dual Mode Rendering: Switch between edit mode (editable) and view mode (read-only display)
  • Document Links: Click inline doc links to open PDFs in resizable side panel
  • Schema-Driven: Define forms declaratively in JSON
  • Config-Based Styling: INI configuration file for consistent styling across projects
  • Computed Fields: Automatic calculation based on other field values
  • Table Fields: Dynamic arrays with configurable columns
  • Built-in Validation: Required fields, min/max, length constraints, custom validators
  • TypeScript: Full type safety and IntelliSense support
  • Extensible: Register custom field renderers without forking

Installation

npm install hazo_data_forms react react-dom react-hook-form lucide-react

# Optional: For PDF viewer support
npm install hazo_pdf

# Optional: For server-side config loading
npm install hazo_config

Important: This library requires Tailwind CSS to be configured in your project. See the Quick Start section for setup instructions.

Peer Dependencies

Required Versions

  • React: ^18.0.0 or ^19.0.0
  • react-hook-form: ^7.0.0
  • hazo_config: ^1.0.0
  • hazo_pdf: ^1.0.0 (optional)

Quick Start

1. Configure Tailwind CSS

This library uses Tailwind CSS classes with semantic CSS variables compatible with shadcn/ui theming.

Tailwind v4: The library uses semantic color classes (bg-muted, text-primary, border-destructive, etc.) that work with Tailwind v4. If you experience missing utility class styles, add the package to your CSS source paths:

/* app/globals.css */
@import "tailwindcss";

/* Include hazo_data_forms for Tailwind to scan utility classes */
@source "../node_modules/hazo_data_forms/dist";

Tailwind v3: Add the library's source to your Tailwind config:

// tailwind.config.js
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
    "./node_modules/hazo_data_forms/dist/**/*.{js,mjs,cjs}",
  ],
  // ... rest of your config
};

Note: This library expects shadcn/ui CSS variables to be defined (e.g., --background, --foreground, --primary, --muted, --destructive, etc.). If you're using shadcn/ui, these are already configured. Otherwise, see the shadcn/ui theming documentation for the required CSS variables.

2. Import the component

import { HazoDataForm, FormSchema } from "hazo_data_forms";

3. Define your form schema

const schema: FormSchema = [
  {
    section_name: "Personal Information",
    sub_sections: [
      {
        sub_section_id: "basic_info",
        sub_section_label: "Basic Details",
        field_group: {
          orientation: "vertical",
          fields: [
            {
              id: "first_name",
              label: "First Name",
              field_info: {
                field_type: "text",
                required: true,
                placeholder: "Enter your first name"
              }
            },
            {
              id: "email",
              label: "Email Address",
              field_info: {
                field_type: "email",
                required: true
              }
            },
            {
              id: "birth_date",
              label: "Date of Birth",
              field_info: {
                field_type: "date"
              }
            }
          ]
        }
      }
    ]
  }
];

3. Render the form

function MyForm() {
  const handle_submit = (values: FormValues) => {
    console.log("Form submitted:", values);
  };

  return (
    <HazoDataForm
      schema={schema}
      mode="edit"
      on_submit={handle_submit}
    />
  );
}

Field Types

Text Fields

// Basic text
{
  id: "username",
  label: "Username",
  field_info: {
    field_type: "text",
    required: true,
    min_length: 3,
    max_length: 20
  }
}

// Email
{
  id: "email",
  label: "Email",
  field_info: { field_type: "email" }
}

// Phone
{
  id: "phone",
  label: "Phone Number",
  field_info: { field_type: "tel" }
}

// Textarea
{
  id: "comments",
  label: "Comments",
  field_info: {
    field_type: "textarea",
    rows: 4,
    max_length: 500
  }
}

Numeric Fields

// Number
{
  id: "age",
  label: "Age",
  field_info: {
    field_type: "number",
    min: 0,
    max: 120
  }
}

// Currency
{
  id: "salary",
  label: "Annual Salary",
  field_info: {
    field_type: "currency",
    currency_symbol: "$",
    decimal_places: 2
  }
}

// Percentage
{
  id: "discount",
  label: "Discount Rate",
  field_info: {
    field_type: "percentage",
    decimal_places: 1,
    min: 0,
    max: 100
  }
}

Selection Fields

// Boolean (checkbox)
{
  id: "agree_terms",
  label: "I agree to the terms and conditions",
  field_info: {
    field_type: "boolean",
    required: true
  }
}

// Option (dropdown)
{
  id: "country",
  label: "Country",
  field_info: {
    field_type: "option",
    options: [
      { label: "United States", value: "US" },
      { label: "Canada", value: "CA" },
      { label: "United Kingdom", value: "UK" }
    ],
    required: true
  }
}

Date Field

{
  id: "start_date",
  label: "Start Date",
  field_info: {
    field_type: "date",
    required: true
  }
}

Computed Field

{
  id: "total",
  label: "Total Amount",
  field_info: {
    field_type: "computed",
    computed_formula: "price * quantity * (1 - discount / 100)",
    computed_dependencies: ["price", "quantity", "discount"]
  }
}

Table Field (Arrays)

{
  id: "line_items",
  label: "Line Items",
  field_info: {
    field_type: "table",
    table_columns: [
      {
        id: "description",
        label: "Description",
        field_info: { field_type: "text" },
        width: "40%"
      },
      {
        id: "quantity",
        label: "Qty",
        field_info: { field_type: "number", min: 1 },
        width: "20%"
      },
      {
        id: "unit_price",
        label: "Unit Price",
        field_info: { field_type: "currency" },
        width: "20%"
      },
      {
        id: "total",
        label: "Total",
        field_info: {
          field_type: "computed",
          computed_formula: "quantity * unit_price"
        },
        width: "20%"
      }
    ],
    table_min_rows: 1,
    table_max_rows: 50
  }
}

Document Links

Add inline document links that open PDFs in an embedded viewer:

{
  id: "contract_value",
  label: "Contract Value",
  field_info: { field_type: "currency" },
  doc_links: [
    {
      type: "pdf",
      url: "/documents/contract.pdf",
      page: 3  // Optional: Open to specific page
    }
  ]
}

PDF Panel Configuration

<HazoDataForm
  schema={schema}
  show_pdf_panel={true}
  pdf_panel_position="right"  // "right" | "left" | "bottom"
  pdf_panel_width="500px"
  pdf_panel_resizable={true}
/>

Form Modes

Edit Mode (Editable)

<HazoDataForm
  schema={schema}
  mode="edit"
  on_submit={(values) => {
    // Handle form submission
    console.log(values);
  }}
/>

View Mode (Read-Only)

const existing_data = {
  first_name: "John",
  email: "[email protected]",
  birth_date: "1990-05-15"
};

<HazoDataForm
  schema={schema}
  mode="view"
  values={existing_data}
/>

Configuration

Using INI Configuration File

Create a configuration file (e.g., /public/config/hazo_data_forms_config.ini):

[colors]
label_color = #374151
field_border_color = #d1d5db
field_border_color_focus = #3b82f6

[fonts]
label_font_size = 14px
field_font_size = 14px

[spacing]
section_spacing = 32px
field_spacing = 16px

[formatting]
default_currency_symbol = $
date_format = MMM d, yyyy

Pass the path to your component:

<HazoDataForm
  schema={schema}
  config_path="/config/hazo_data_forms_config.ini"
/>

Runtime Configuration Override

<HazoDataForm
  schema={schema}
  config_override={{
    label_color: "#1e40af",
    field_border_color: "#60a5fa",
    default_currency_symbol: "€",
    date_format: "dd/MM/yyyy"
  }}
/>

API Reference

HazoDataForm Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | schema | FormSchema | required | Form schema defining sections and fields | | mode | "edit" \| "view" | "edit" | Form mode (edit allows input, view is read-only) | | values | FormValues | {} | Controlled form values | | default_values | FormValues | {} | Default values for uncontrolled form | | on_change | (values: FormValues) => void | - | Callback when any field value changes | | on_field_change | (field_id: string, value: unknown) => void | - | Callback when specific field changes | | on_submit | (values: FormValues) => void | - | Callback when form is submitted | | on_doc_link_click | (doc_link: DocLink) => void | - | Callback when document link is clicked | | show_pdf_panel | boolean | true | Whether to show PDF panel for doc links | | pdf_panel_position | "left" \| "right" | "right" | Position of PDF panel | | pdf_panel_width | string | from config | Width of PDF panel | | pdf_panel_resizable | boolean | true | Whether PDF panel is resizable | | config_path | string | - | Path to INI configuration file | | config_override | PartialFormConfig | - | Runtime configuration overrides | | errors | FormErrors | - | External validation errors | | validate_on_blur | boolean | true | Validate fields on blur | | validate_on_change | boolean | false | Validate fields on change | | validate | (values: FormValues) => FormErrors | - | Custom validation function | | class_name | string | - | Additional CSS class for form container | | show_section_headers | boolean | true | Show section headers | | show_sub_section_headers | boolean | true | Show sub-section headers | | collapsible_sections | boolean | false | Make sections collapsible | | collapsed_sections | string[] | [] | IDs of initially collapsed sections | | on_form_ready | (methods: UseFormReturn) => void | - | Callback with react-hook-form methods | | show_submit_button | boolean | - | Show submit button at bottom of form | | submit_button_text | string | "Submit" | Text for submit button |

Custom Field Renderers

Register custom field types:

import { register_field_renderer, type FieldRendererProps } from "hazo_data_forms";

function CustomField({ field, value, on_change, mode, config }: FieldRendererProps) {
  // Your custom field implementation
  return <div>...</div>;
}

register_field_renderer("custom_type", CustomField);

Then use in your schema:

{
  id: "my_field",
  label: "My Custom Field",
  field_info: {
    field_type: "custom_type" as any
  }
}

Exported Types

import type {
  // Schema types
  FormSchema,
  FormSection,
  SubSection,
  FieldGroup,
  FormField,
  FieldInfo,
  FieldType,
  OptionItem,
  TableColumn,

  // Runtime types
  FormValues,
  FormErrors,
  FormMode,
  DocLink,
  DocLinkClickEvent,
  PdfPanelPosition,

  // Configuration
  FormConfig,
  PartialFormConfig,

  // Component props
  HazoDataFormProps,
  FieldRendererProps,
  FieldRenderer,
} from "hazo_data_forms";

Exported Utilities

import {
  // Styling
  cn,

  // Formatting
  format_currency,
  format_percentage,
  format_date,

  // Parsing
  parse_boolean,
  parse_number,
  parse_string,

  // Computed fields
  evaluate_formula,

  // Utilities
  generate_id,
  deep_merge,
} from "hazo_data_forms";

Styling

This library uses Tailwind CSS with semantic CSS variables for styling, ensuring compatibility with both Tailwind v3 and v4. The library uses shadcn/ui-compatible color tokens:

  • Background colors: bg-background, bg-muted, bg-primary/10, bg-destructive/10
  • Text colors: text-foreground, text-muted-foreground, text-primary, text-destructive
  • Border colors: border-border, border-input, border-primary, border-destructive

All CSS classes use the cls_ prefix following hazo ecosystem conventions:

  • cls_hazo_data_form - Main form container
  • cls_section - Section container
  • cls_sub_section - Sub-section container
  • cls_field_group - Field group container
  • cls_field_wrapper - Individual field wrapper
  • cls_field_label - Field label
  • cls_field_input - Field input element
  • cls_doc_link_button - Document link button
  • cls_pdf_panel - PDF panel container

You can override these styles in your own CSS or use the configuration system to customize colors, fonts, and spacing.

Examples

Complete Form Example

See the test-app directory in the repository for a complete Next.js example with various field types and PDF integration.

View Mode

<HazoDataForm
  schema={schema}
  mode="view"
  values={savedData}
/>

Validation

<HazoDataForm
  schema={schema}
  validate={(values) => {
    const errors: FormErrors = {};
    if (!values.email?.includes("@")) {
      errors.email = "Invalid email address";
    }
    return errors;
  }}
  validate_on_blur={true}
/>

Accessing Form Methods

function MyForm() {
  const formMethodsRef = React.useRef<UseFormReturn | null>(null);

  return (
    <HazoDataForm
      schema={schema}
      on_form_ready={(methods) => {
        formMethodsRef.current = methods;
      }}
    />
  );
}

License

MIT

Repository

https://github.com/pub12/hazo_data_forms