Library / SDK
hasanharman/form-builder avatar
hasanharman/form-builder

shadcn-form-builder: a drag-and-drop form canvas built on shadcn/ui

A dynamic form-building tool that allows users to create, customize, and validate forms seamlessly within web applications.

2,694 stars264 forksTypeScriptMIT

At a glance

What is it?
A Next.js app that lets users compose forms from a toolbar of field types, validate them with Zod, and submit them to a JSON API. The interesting parts are the field component contract and the validation bridge, not the drag and drop.
Who is it for?
shadcn-form-builder earns a look if you are building a form experience on top of shadcn/ui and want the field rendering, the validation and the toolbar to share one type contract instead of three. It ships as an MIT-licensed reference application with a `registry/` directory and a `proxy.ts` at the repository root, it is not archived, and the last push was on 2026-06-28.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 100 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A reference app, not a form engine

The README is short and the repository is small enough to read in one sitting, which makes it easy to misjudge the project. Form Builder is not a hosted form product with a backend. It is a Next.js application that demonstrates how to put together a drag-and-drop form designer using React, Next.js, Tailwind CSS and shadcn/ui components, with Zod handling validation. The live demo at `shadcn-form.com` is the same application running.

That framing matters for anyone evaluating it, because a reference app answers a different question than a library does. It shows you how the pieces fit, and the pieces themselves come from elsewhere. Validation is Zod. The UI primitives are Radix components wrapped by shadcn/ui. Form state comes from `@tanstack/react-form` with the `@tanstack/zod-form-adapter` bridging the two, and `react-hook-form` resolvers are pulled in as well. What the project actually contributes is the layer in the middle: a canvas, a toolbar, and a set of field components that know how to describe themselves.

The license is MIT, the repository is not archived, and the last push was on 2026-06-28.

Getting it running is three commands

The installation section is short enough to quote directly. Clone the repository, move into the directory, and install dependencies:

bash
git clone https://github.com/hasanharman/form-builder.git
cd form-builder
npm install

The README then starts the development server with `npm run dev` and points the browser at `http://localhost:3000`. Worth noting that `package.json` pins the package name to `shadcn-form-builder` at version `0.1.0` and marks it private, and that a `pnpm-lock.yaml` sits at the repository root even though the README says `npm install`. Either package manager works; the lockfile suggests pnpm is what the project settles its versions with.

The scripts block is where the Next.js version choice shows up. Every script passes `--webpack` explicitly:

json
    "dev": "next dev --webpack",
    "build": "next build --webpack",
    "postbuild": "next-sitemap",
    "start": "next start",
    "lint": "next lint",
    "test": "vitest"

That is not incidental. The opt-in flag sidesteps the bundler migration entirely, which matters if you are copying this setup into a project that has other moving parts. The `postbuild` hook runs `next-sitemap` and `next-sitemap.config.js` is in the tree, so sitemap generation is wired to the build rather than run by hand.

The field component contract

The README lists the component set, and the list is worth reading closely because it tells you where the extension points are:

- `FormContainer`, described as the main container for the form elements - `InputField`, a customizable input component - `SelectField`, a dropdown selection component - `CheckboxField`, a checkbox input component - `Button`, a styled button component for form submission

The word `FormContainer` is doing more work than it appears to. If it is genuinely the container through which every field renders, then adding a field type means writing one component that speaks the same contract as the existing four, and the canvas does not need to learn about it. That is the shape you want from a form builder, and it is the reason the dependency list includes `@radix-ui/react-slot`, which lets a wrapper pass props through to the underlying primitive instead of forking it.

The dependency list also tells you what kind of editing surface you are building against. There are twenty-plus Radix packages in `dependencies`, covering accordion, avatar, checkbox, context menu, dialog, dropdown menu, label, popover, progress, radio group, scroll area, select, separator, slider, switch, tabs, toast and tooltip. A toolbar that can offer a field type is also a toolbar that needs popovers, dialogs and tooltips to stay usable on a small screen.

The repository tree supports the same reading. `components/` holds the UI, `registry/` is the shadcn registry configuration, `context/` and `data/` hold shared state, `screens/` holds the page-level surfaces, and `constants/` holds the field type definitions that keep the toolbar and the renderer in agreement.

Validation runs through Zod

The validation story is the clearest part of the whole project, because it delegates to a library rather than inventing a rule engine. The README shows a schema and says it can be applied to enforce validation rules:

javascript
import { z } from 'zod';

const formSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email address"),
  age: z.number().min(18, "You must be at least 18 years old"),
});

Two details in that snippet matter more than they first appear. The custom messages are passed as the second argument to each validator, which means the user-facing copy travels with the rule instead of living in a separate dictionary. And the schema is plain Zod with no builder-specific wrapper, so the same object can validate on the server.

The dependency graph explains how that schema reaches the form. `@tanstack/zod-form-adapter` is the piece that lets form state read a Zod schema directly, and `@hookform/resolvers` is pinned to an exact version at `5.0.1`, which suggests the react-hook-form path is there as well. The README frames this as real-time validation with user-friendly feedback rather than submit-time validation, so the schema is evaluated as fields change.

For a canvas-driven builder, the real question is where the schema comes from at runtime. In a hand-written form you define it in code. In a builder the user defines it in the UI, so something has to serialize the field configuration into a Zod object. `constants/` and `types.ts` at the repository root are the natural home for that mapping, and it is the piece to look at first if you want to extend the project.

Submission is left as an example

The API section is a single handler, and it is worth being precise about what it does and does not establish:

javascript
const handleSubmit = async (data) => {
  try {
    const response = await fetch('/api/form-submit', {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
      },
    });
    const result = await response.json();
    console.log('Form submitted successfully:', result);
  } catch (error) {
    console.error('Error submitting form:', error);
  }
};

It posts JSON to `/api/form-submit`. The repository tree lists `app/`, `proxy.ts` and `scripts/`, but there is no route described for that path in the README, so the example is illustrative. If you take this project as a starting point, the submit path is the part you will replace first.

What the snippet does get right is the shape. A single call with an explicit `Content-Type`, the response parsed back as JSON, and the error path kept separate from the success path. There is no retry logic and no abort handling, which is appropriate for an example and worth adding in production, especially since a form builder sits directly on the front door of an application.

The blocknote packages in the dependency list, at `0.20.0`, suggest a rich text field type exists or is planned, which fits with a form builder that aims past plain inputs into survey and article style forms.

Where to extend it

The toolbar approach to adding inputs is described in four steps: reach the builder interface, add input types from the toolbar, click a field to configure its label, placeholder and required validation, then save and preview. Those four steps define the extension surface, because each one corresponds to a distinct piece of code.

Adding a field type is a component plus an entry in the field type list. Making a field configurable means the selected field has to expose its properties to an editor panel, which means the field configuration has to be data rather than props baked in at render time. Save and preview means the built form has a serializable representation, and that same representation is what a Zod schema would be generated from.

The three test-adjacent files in the tree, `vitest.config.ts` and the `__tests__/` directory, are thin, which is expected for a demonstration application. The `test` script is a bare `vitest` with no arguments, so it runs in watch mode locally.

Two things are worth deciding early if you build on this. Whether the built form is saved to local storage, to a database or to an API changes the shape of everything downstream of save. And whether validation is client-only or mirrored on the server decides whether the Zod schema has to be serializable, which would push you toward sharing schema definitions with the server instead of rebuilding them from field config. The dependency set already supports the second option, since the same Zod object can run in both places.

Editorial conclusion

shadcn-form-builder earns a look if you are building a form experience on top of shadcn/ui and want the field rendering, the validation and the toolbar to share one type contract instead of three. It ships as an MIT-licensed reference application with a `registry/` directory and a `proxy.ts` at the repository root, it is not archived, and the last push was on 2026-06-28. Run `npm install` then `npm run dev`, open the builder, add fields from the toolbar, and read `FormContainer` first, because every field type in `components/` is rendered through it. Leave the submit path alone and wire it to your own endpoint, since the README's example posts to a route the repository does not define.

Frequently asked questions

Is the shadcn-form-builder project free to use?

Yes. The repository is licensed under the MIT License, and the `package.json` marks the package private at version `0.1.0`, which means the code is free to use and modify but is not published to a registry as a reusable package.

What stack is shadcn-form-builder built on?

React with Next.js, styled with Tailwind CSS and shadcn/ui components on top of Radix primitives. Form state uses `@tanstack/react-form` with `@tanstack/zod-form-adapter` for validation, and the scripts pass `--webpack` to every Next.js command.

How does shadcn-form-builder validate user input?

Through Zod. The README shows a `z.object` schema with custom messages passed to each validator, and the adapter packages in `dependencies` let form state read that schema directly so validation can run as the user types.

Can shadcn-form-builder submit forms to a backend?

The README shows a handler that POSTs JSON to `/api/form-submit`, but it is an example rather than a documented endpoint, so expect to point the submit path at your own route. There is no built-in storage layer described in the repository.

How do I add a new field type to shadcn-form-builder?

Write a component that follows the same contract as `InputField`, `SelectField` and `CheckboxField`, then register it so it appears in the toolbar. `FormContainer` is the rendering container and `constants/` plus `types.ts` are where field type definitions live.

Official sources

  1. hasanharman/form-builder on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hasanharman-form-builder.svg)](https://hysenlabs.com/projects/hasanharman-form-builder)