Skip to content
Neon UI
Esc
navigateopen⌘Jpreview
On this page

SchemaExplorer

A collapsible Postgres schema tree from tables to columns, indexes, and relations, with types and search.

SchemaExplorer browses a database’s schema as a keyboard-native tree. Each table expands to its columns (with SQL types, primary keys, foreign keys, unique constraints, and nullability), its indexes, and its relations. Search reveals matching descendants and highlights the match. It is presentational: the schema comes from your backend, usually via information_schema introspection.

public

3 tables

Behavior

  • Expand click a table to reveal its columns, indexes, and relations. Several tables can stay open at once.
  • Types each column shows its SQL type; primary keys wear a key, unique columns a unique tag, and nullable columns a faint null.
  • Relations foreign-key columns are listed under Relations as column → table.column.
  • Search press / from the tree, then search table names, column names, index names, or Postgres types. Matching descendants open automatically.
  • Keyboard use Up/Down to move, Right to open or enter a table, Left to close or return to its parent, Home/End to jump, and typeahead to find a visible row.

Install

npx shadcn@latest add https://ui.neon.com/r/schema-explorer.json

Usage

import { SchemaExplorer } from "@/components/neon-ui/schema-explorer";

<SchemaExplorer tables={tables} defaultExpanded={["users"]} />;

tables is an array of { name, columns, indexes?, rowCount? }. Each column is { name, type, primaryKey?, unique?, nullable?, references? }.

Wiring to Neon

A server component introspects the public schema over the Neon serverless driver and maps the rows to tables:

import { neon } from "@neondatabase/serverless";

const sql = neon(process.env.DATABASE_URL);
const rows = await sql`
  SELECT table_name, column_name, data_type, is_nullable
  FROM information_schema.columns
  WHERE table_schema = 'public'
  ORDER BY table_name, ordinal_position
`;
// group rows by table_name into { name, columns } and add PK/FK from
// information_schema.table_constraints

It runs server-side, so the connection string never reaches the browser. Add indexes from pg_indexes if you want the Indexes section populated.

Props

PropType
tablesTable[]

The tables to browse. Each is { name, schema?, columns, indexes?, rowCount? }.

TypeTable[]
defaultExpanded?string[]

Table names expanded on first render.

Typestring[]
expanded?string[]

Expanded table names when controlled.

Typestring[]
onExpandedChange?(expanded: string[]) => void

Called whenever a table opens or closes.

Type(expanded: string[]) => void
onColumnSelect?(table: Table, column: Column) => void

Called when a column row is activated.

Type(table: Table, column: Column) => void
onIndexSelect?(table: Table, index: Index) => void

Called when an index row is activated.

Type(table: Table, index: Index) => void
title?string

Panel heading (typically the schema name).

Typestring
Default"public"

Accessibility

The component follows the WAI-ARIA tree pattern with one Tab stop, roving focus, arrow-key navigation, Home/End, typeahead, and aria-expanded on table rows. Focus stays visible over hover and open states. The only motion is the chevron turn, which snaps under reduced motion.