Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Modal Create Form Block for React

A project manager's Projects page with its New project dialog shown open: name, description and category, and a discard guard that swaps the footer for Keep editing and Discard when a dirty form is dismissed.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Create a record without losing a half-written one, with this modal create form for React with Base UI. It opens from the Projects page of Kiteboard, a project manager: the page's header holds an outline New project Button, and the page itself is a drawn table under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog holds a name (Input, 3 to 60 characters, not one that exists), an optional description (Textarea, 140 characters or fewer) and a category, each in a Field with a reserved line for its hint or problem. Escape, the scrim, the close button or Cancel on a clean form close it at once. On a dirty form the footer swaps, in the same space, for "Discard this project?" with Keep editing, where focus lands, and Discard; a second Escape means keep editing. Create project, the only Shine Button, checks the fields, adds a row to the table behind, closes and says so. For preview the dialog is drawn open in place when the page loads; New project opens the real Dialog, with the same fields and state. For projects, workspaces, labels and any create form in a modal.

FAQ

Why is the dialog already open, and what do I keep?

So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that is not modal, so nothing is trapped, hidden or focused. The New project button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same ProjectFields component and share the block's state, so what you typed in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

How does the discard guard work?

Every way out except Create goes through one function, ask. On a clean form it closes. On a dirty one (anything typed or chosen) it keeps the dialog open and swaps the footer for the question, which sits in the same grid cell as the footer so the dialog never changes height; focus goes to Keep editing and a polite message says what happened. Keep editing, or Escape again, goes back to the fields; Discard closes and clears the form. The dialog is controlled, so Escape and the scrim reach ask through the Dialog's own open handler.

What does Create project do, and how do I wire it up?

It checks the name (3 to 60 characters, and not the name of a project in the list), the description length and that a category is chosen. A problem shows under its field and focus goes to the first one; otherwise a row is added at the top of the drawn table, the dialog closes, focus returns to New project and a polite message says "Created Spring launch in Product. Nothing was saved in this demo." In your app post the values from create, wait for the server, add the row from the response and show a server refusal (a name that is taken) in the field's slot.

Is it accessible from the keyboard and to screen readers?

Yes. The real dialog is named by its title and described by its sentence, focus starts in the name field and stays inside until it closes, and every close returns focus to New project. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. Because a modal hides the page behind it, a second polite status inside the dialog speaks while it is open.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet: the fields scroll above, and the footer stays pinned with its two buttons side by side. The real dialog keeps the Dialog's own phone layout, stuck to the bottom of the screen. The discard question uses the same footer space, with the question above the two buttons.

How do I connect it to react-hook-form or TanStack Form?

Keep the three values in the form and read its dirty state for the guard. With react-hook-form, formState.isDirty replaces isDirty here, handleSubmit(create) replaces the form's onSubmit and reset() replaces reset. With TanStack Form, use form.state.isDirty, validate each field on blur and call form.reset() from Discard.

  • Pro

    Invite Members Dialog

    Share and invite

    A household calendar's Members page with its invite dialog shown open: one email field that knows the household's domain, so an outside address joins as a guest, the people who already have access, and a Send invite button that adds a pending row.

  • Pro

    Add Card Dialog

    Payments and plans

    A transit wallet's payment methods page with its add-card dialog shown open: a card drawn in greys that fills in as you type, a number that groups itself and names its network, checks on the number, expiry and code, and an Add card button that keeps only the last four digits.

  • Pro

    Settings Drawer Form

    Dialogs and layouts

    A longer settings form in a bottom drawer with a fixed header and footer, soft shadows at the scrolling edges, a count of unsaved changes worked out from the fields and a guard against closing a dirty form.

  • Pro

    A form panel with one required project-name field that shows the web address it will get as you type, flags a name that is taken or past 50 characters, and offers the next free address.

  • Pro

    Confirm Delete Dialog

    Destructive confirms

    A planner's project list with its delete confirmation shown open: an alert dialog that lists what goes with the project, asks for its name to be typed exactly, and keeps Delete off until it matches.

  • Pro

    Review and Submit Form

    Dialogs and layouts

    A review step: three sections of typed answers that each edit in place in the same boxes, a side cell of what changed, a Submit that names the section to finish, and a lock after a local submit.