Database

Database

Databases as plain data plus React views: a typed collection schema, rows, saved views with filters and sorts, a state hook and seven layouts.

Tasks
Design tokens audit
Sidebar rebuild
Command palette
Docs shell
Icon extraction
Database views
Screenshot diffing
Release notes

Data model

Everything a database renders comes from one DatabaseData object: { collection, rows, views }.

  • Collection: { id, name, properties: Property[] }, the typed schema.
  • Property: a discriminated union on type: title, text, number (format), select / multi_select (options), status (options with a group: todo / in_progress / complete), date (dateFormat), person (people), checkbox, url, email, phone, created_time.
  • Row: { id, values, icon? }. values is keyed by property id; dates are ISO YYYY-MM-DD, select/status store option ids, multi-select and person store id arrays.
  • View: { id, name, type, filters, sorts, groupBy?, visible?, widths?, dateProperty?, order?, chart? }. type is one of table, board, gallery, list, calendar, timeline, chart.
  • Filter: { id, propertyId, operator, value? } with operators is, is_not, contains, does_not_contain, is_empty, is_not_empty, before, after, checked, unchecked, gt, lt.
  • Sort: { propertyId, direction: "ascending" | "descending" }, applied in order.
  • OptionColor: the option colors (default, gray, brown, orange, yellow, green, blue, purple, pink, red), mapped to token classes by optionColors.
/lib/projects.ts
import type { DatabaseData } from '@krackedui/motion';

export const projects: DatabaseData = {
  collection: {
    id: 'projects',
    name: 'Projects',
    properties: [
      { id: 'title', name: 'Project', type: 'title' },
      { id: 'owner', name: 'Owner', type: 'person', people: [{ id: 'ada', name: 'Ada Lovelace' }] },
      {
        id: 'stage', name: 'Stage', type: 'status', options: [
          { id: 'idea', name: 'Idea', color: 'gray', group: 'todo' },
          { id: 'build', name: 'Building', color: 'blue', group: 'in_progress' },
          { id: 'live', name: 'Live', color: 'green', group: 'complete' },
        ],
      },
      { id: 'launch', name: 'Launch', type: 'date' },
    ],
  },
  rows: [
    { id: 'p1', values: { title: 'Q4 roadmap', owner: ['ada'], stage: 'build', launch: '2026-11-03' } },
  ],
  views: [
    { id: 'all', name: 'All projects', type: 'table', filters: [], sorts: [] },
    { id: 'board', name: 'By stage', type: 'board', filters: [], sorts: [], groupBy: 'stage' },
  ],
};

State: useDatabase

useDatabase(initial, initialViewId?) holds one database in React state (no persistence) and returns a DatabaseApi. Pass it to <DatabaseView db={...}> to share state between several views, or let DatabaseView create its own from data.

Views, cells and menus read the API through context: useDatabaseContext() (throws outside a database) or useOptionalDatabase() (returns null, so menus can also render on their own).

Query helpers

  • queryRows(collection, rows, view): filter, then sort, then the view’s manual order.
  • groupRows(collection, rows, groupBy): RowGroup[] per option (plus "No …" for empty values).
  • matchesFilter, compareRows, visibleProperties, titleProperty, rowTitle, getProperty, findOption, optionsOf.
  • operatorsFor[type] and operatorLabels[op] drive the filter editor; valuesFromFilters pre-fills new rows so they match the active filters.
  • Dates: parseDate, toISODate, formatDate, monthLabel, sameDay, monthGrid.

Sample data

createTasksSample() returns a fresh copy of the sample database "Tasks": 5 properties (Task, Status, Priority, Team, Due), 8 rows and one view of each type (v-table, v-board, v-gallery, v-list, v-calendar, v-timeline, v-chart). SAMPLE_TODAY is the sample’s reference date, 2026-10-15; pass it as today so calendar and timeline show the sample rows around today.

Usage

  • Use the database module when content is a collection of records that people sort, filter and view in several layouts (tasks, projects, CRM rows).
  • Start from DatabaseView with a DatabaseData object; reach for the individual views and menus only when you build your own database chrome.
  • Share one useDatabase() state between several views with <DatabaseView db={…}> so edits stay in sync.
  • Don’t use a database for a short static list or a single record; use a plain Table, a list or a property list instead.
  • Don’t persist through useDatabase; it holds state in memory. Save db.data to your backend yourself.

Installation

Install the package (React 19):

terminal
npm install @krackedui/motion

Import the styles once, in your app entry or root layout:

/src/main.tsx
import '@krackedui/motion/styles.css';

Use it anywhere inside your app's <Theme> (see Installation):

/src/DatabaseExample.tsx
import { DatabaseView, createTasksSample, SAMPLE_TODAY } from '@krackedui/motion';

export function TasksDatabase() {
  return <DatabaseView data={createTasksSample()} today={SAMPLE_TODAY} />;
}

Examples

Shared state across views

One useDatabase instance drives two blocks: edit a status in the table and the board follows.

Tasks
Design tokens audit
Sidebar rebuild
Command palette
Docs shell
Icon extraction
Database views
Screenshot diffing
Release notes
Not started
4
In progress
2
Done
2

API Reference

useDatabase

useDatabase(initial: DatabaseData | (() => DatabaseData), initialViewId?: string): DatabaseApi. The returned API:

PropTypeDefault
data
DatabaseData
activeViewId
string
activeView
View
setActiveView
(id: string) => void
updateView
(id: string, patch: Partial<View> | ((v: View) => Partial<View>)) => void
addView
(type: ViewType) => View
updateRow
(rowId: string, propertyId: string, value: PropertyValue) => void
addRow
(values?: Row['values'], opts?: { viewId?: string; position?: 'start' | 'end' }) => Row
updateProperty
(id: string, patch: Partial<Property>) => void
addOption
(propertyId: string, name: string) => SelectOption
moveRow
(viewId: string, rowId: string, beforeRowId?: string) => void

DatabaseProvider

Puts a DatabaseApi in context for bare views and cells. Read it with useDatabaseContext() or useOptionalDatabase().

PropTypeDefault
value
DatabaseApi
children
ReactNode

View

A saved layout of the collection.

PropTypeDefault
id
string
name
string
type
"table""board""gallery""list""calendar""timeline""chart"
filters
Filter[]
sorts
Sort[]
groupBy
string
visible
string[]
all
widths
Record<string, number>
title 280, others 200
dateProperty
string
first date property
order
string[]
chart
{ locked?: boolean; groupBy?: string }

Accessibility

  • Views render real roles: the table is role="table" with row / columnheader / cell; board columns are role="list" with listitem cards; calendar is a grid.
  • View tabs are a tablist of tab buttons with aria-selected; keyboard focus shows a 2px + 4px blue ring.
  • Menus are focusable surfaces: ArrowUp/ArrowDown move, Enter/Space activate (useMenuKeys). Popovers (Radix) close on Escape and return focus to the trigger.
© 2026 Motion. Internal documentation.