BranchPicker
A searchable Neon branch selector with default and protected status and an inline create-branch affordance.
BranchPicker is a searchable selector for a project’s Neon branches, drawn as the branch tree so you can see what is a branch of what. Open it, type to filter, pick a branch, or create a new branch from any branch. The default branch wears a default badge, protected branches show a lock, and the current choice carries a check. It is presentational: the branches come from your backend as plain data.
Behavior
- Graph with no search, branches are drawn as a real graph: each is a node in the lane of its depth, with a curved edge from its parent. Searching collapses it to a flat filtered list.
- Keyboard the arrow keys move through the list, Enter selects the highlighted branch, and Escape closes.
- Branch from with
onCreateBranchset, each row reveals a+on hover: it opens a “New branch from ⟨name⟩” field, so a branch is always created from a chosen parent. - Status
defaultandprotectedrender inline; the selected branch shows a check.
Install
npx shadcn@latest add https://ui.neon.com/r/branch-picker.json
Usage
import { BranchPicker } from "@/components/neon-ui/branch-picker";
<BranchPicker
branches={branches}
value={branchId}
onValueChange={setBranchId}
onCreateBranch={(name, fromId) => createBranch(name, fromId)}
/>;
Controlled with value + onValueChange, or uncontrolled with defaultValue. Omit onCreateBranch to hide the create affordance.
Wiring to Neon
A server component lists a project’s branches and selects the default:
import { createNeonClient } from "@/lib/neon-client";
const neon = createNeonClient(process.env.NEON_API_KEY);
const { data } = await neon.branches.list(projectId).all();
// map each branch to { id, name, default, protected, parent }
Wire onValueChange and onCreateBranch in a client wrapper or as server actions, since they mutate.
Props
branchesBranch[]
The branches to choose from. Each is { id, name, default?, protected?, parent? }, where parent is the parent branch's id.
Branch[]value?string
Selected branch id (controlled).
stringdefaultValue?string
Initial selected branch id (uncontrolled).
stringonValueChange?(id: string) => void
Notified when a branch is chosen.
(id: string) => voidonCreateBranch?(name: string, fromId: string) => void
Create a branch with the typed name off the chosen parent branch. The per-branch affordance hides when omitted.
(name: string, fromId: string) => voidplaceholder?string
Trigger text when nothing is selected.
string"Select branch"Accessibility
The trigger is a button with aria-haspopup and aria-expanded; the list is an ARIA listbox of option rows with the selected one marked; and the search and create fields are real inputs. Arrow keys and Enter drive selection from the keyboard. Focus lands on the search field when the picker opens, and on the name field when you branch from a row.