Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,39 @@ A **Table Schema builder**, **BYOS (Bring Your Own Store)** state management, an

![Data Table with Infinite Scroll](https://data-table.openstatus.dev/assets/data-table-infinite.png)

Visit [data-table.openstatus.dev](https://data-table.openstatus.dev) to learn more. Read the [Guide](https://data-table.openstatus.dev/guide) for full documentation.
Visit [data-table.openstatus.dev](https://data-table.openstatus.dev) to learn more. Read the [Docs](https://data-table.openstatus.dev/docs) for full documentation.

## Install

Install blocks via the shadcn registry:

```bash
npx shadcn@latest add https://data-table.openstatus.dev/r/data-table.json
```

| Block | Install URL | What it adds |
| --------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `data-table` | `.../r/data-table.json` | Core: table engine, store, 4 filter types, memory adapter |
| `data-table-filter-command` | `.../r/data-table-filter-command.json` | Command palette with history + keyboard shortcuts |
| `data-table-cell` | `.../r/data-table-cell.json` | 8 cell renderers (text, code, badge, boolean, number, status-code, level-indicator, timestamp) |
| `data-table-sheet` | `.../r/data-table-sheet.json` | Row detail side panel |
| `data-table-nuqs` | `.../r/data-table-nuqs.json` | nuqs URL state adapter |
| `data-table-zustand` | `.../r/data-table-zustand.json` | zustand state adapter |
| `data-table-schema` | `.../r/data-table-schema.json` | Declarative schema system with `col.*` factories |
| `data-table-drizzle` | `.../r/data-table-drizzle.json` | Drizzle ORM server-side helpers |
| `data-table-query` | `.../r/data-table-query.json` | React Query infinite query integration |

All URLs use base `https://data-table.openstatus.dev`.

## Agent Skill

Install the agent skill to let Claude Code (or any compatible AI coding tool) set up data-table-filters in your project:

```bash
npx skills add https://github.com/openstatushq/data-table-filters --skill data-table-filters
```

Then just say "add a filterable data table" — the skill detects your stack, installs the right blocks, generates a schema, and wires everything up.

## Table Schema

Expand Down Expand Up @@ -49,6 +81,7 @@ const sheetFields = generateSheetFields<ColumnSchema>(tableSchema.definition);

- [`/default`](https://data-table.openstatus.dev/default) — client-side pagination (nuqs or zustand)
- [`/infinite`](https://data-table.openstatus.dev/infinite) — infinite scroll with server-side filtering, live mode, row details
- [`/drizzle`](https://data-table.openstatus.dev/drizzle) — Drizzle ORM + Supabase PostgreSQL with cursor-based pagination, faceted search, and live data via Vercel cron
- [`/light`](https://data-table.openstatus.dev/light) — OpenStatus Light Viewer (UI for [`vercel-edge-ping`](https://github.com/OpenStatusHQ/vercel-edge-ping))
- [`/builder`](https://data-table.openstatus.dev/builder) — interactive schema builder (paste JSON/CSV, live table preview, export TS)

Expand All @@ -60,7 +93,7 @@ A pluggable adapter pattern for filter state management. Three built-in adapters
- **zustand** — client-side state (existing store integration)
- **memory** — ephemeral in-memory state (embedded tables, builder)

Or implement the `StoreAdapter` interface for a custom solution. See the [Guide](https://data-table.openstatus.dev/guide) for details.
Or implement the `StoreAdapter` interface for a custom solution. See the [Docs](https://data-table.openstatus.dev/docs) for details.

## Built With

Expand All @@ -71,6 +104,7 @@ Or implement the `StoreAdapter` interface for a custom solution. See the [Guide]
- [cmdk](http://cmdk.paco.me)
- [nuqs](http://nuqs.47ng.com)
- [zustand](https://zustand.docs.pmnd.rs)
- [drizzle-orm](https://orm.drizzle.team)
- [zod](https://zod.dev)
- [superjson](https://github.com/flightcontrolhq/superjson)
- [date-fns](https://date-fns.org)
Expand All @@ -79,7 +113,7 @@ Or implement the `StoreAdapter` interface for a custom solution. See the [Guide]

## Getting Started

No environment variable required.
No environment variable required for the default examples. For the Drizzle example, set `DATABASE_URL` to a PostgreSQL connection string.

```bash
pnpm install
Expand Down
26 changes: 13 additions & 13 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ export default function Home() {
</div>
</div>
<div className="px-2.5 py-2">
<p className="font-medium group-hover:underline">
Infinite Data-Table
</p>
<p className="font-medium">Infinite Data-Table</p>
<p className="text-muted-foreground text-sm">
A{" "}
<span className="underline decoration-blue-500 decoration-wavy underline-offset-2">
cooked
<span className="relative inline-block font-medium text-green-700 italic transition-colors group-hover:text-green-50 dark:text-green-400 dark:group-hover:text-green-950">
<span className="absolute inset-0 scale-x-105 -skew-x-12 bg-green-500/10 transition-colors group-hover:bg-green-600 dark:bg-green-500/10 dark:group-hover:bg-green-400"></span>
<span className="relative">cooked&nbsp;</span>
</span>{" "}
infinite scroll data-table with a{" "}
<span className="text-foreground font-medium">server-side</span>{" "}
Expand Down Expand Up @@ -76,13 +75,12 @@ export default function Home() {
</div>
</div>
<div className="px-2.5 py-2">
<p className="font-medium group-hover:underline">
Default Data-Table
</p>
<p className="font-medium">Default Data-Table</p>
<p className="text-muted-foreground text-sm">
A{" "}
<span className="underline decoration-yellow-500 decoration-wavy underline-offset-2">
simple
<span className="relative inline-block font-medium text-indigo-700 italic transition-colors group-hover:text-indigo-50 dark:text-indigo-400 dark:group-hover:text-indigo-950">
<span className="absolute inset-0 scale-x-105 -skew-x-12 bg-indigo-500/10 transition-colors group-hover:bg-indigo-600 dark:bg-indigo-500/10 dark:group-hover:bg-indigo-400"></span>
<span className="relative"> simple&nbsp;</span>
</span>{" "}
data-table with a{" "}
<span className="text-foreground font-medium">client-side</span>{" "}
Expand Down Expand Up @@ -357,13 +355,15 @@ function Hero() {
.
</h2>
<p className="text-foreground italic sm:text-lg">
It&apos;s not a library. It&apos;s a playbook.{" "}
<span className="mr-2">
It&apos;s not a library. It&apos;s a playbook.{" "}
</span>
<NextLink
href="/docs/introduction"
className="group text-foreground hover:text-background relative ml-2 inline-block font-medium"
className="group text-foreground hover:text-background relative inline-block font-medium"
>
<span className="bg-muted group-hover:bg-foreground absolute inset-0 scale-x-105 -skew-x-12"></span>
<span className="relative">Learn more in the Docs.</span>
<span className="relative">Learn more in the Docs.&nbsp;</span>
</NextLink>
</p>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/app/builder/builder-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ export function BuilderClient() {
{...field}
id={field.name}
className={cn(
"font-mono text-xs",
"field-sizing-fixed font-mono text-xs",
fieldState.invalid && "border-destructive",
)}
rows={7}
Expand Down Expand Up @@ -385,7 +385,7 @@ export function BuilderClient() {
id={field.name}
rows={7}
className={cn(
"font-mono text-xs",
"field-sizing-fixed font-mono text-xs",
fieldState.invalid && "border-destructive",
)}
placeholder='{ "columns": [] }'
Expand Down
2 changes: 1 addition & 1 deletion src/app/docs/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export default async function DocsSectionPage({
className={cn(
"prose prose-lg dark:prose-invert",
"prose-pre:rounded-lg prose-pre:border prose-pre:border-border prose-pre:bg-foreground dark:prose-pre:bg-muted/50",
"prose-blockquote:rounded-lg prose-blockquote:border prose-blockquote:border-border prose-blockquote:bg-muted/50 prose-blockquote:pe-6 prose-blockquote:font-normal prose-blockquote:not-italic",
"prose-blockquote:font-normal prose-blockquote:not-italic prose-blockquote:border-border! prose-blockquote:border-l-2",
"w-full min-w-0 overflow-x-auto",
"prose-h1:font-semibold prose-h2:font-semibold prose-h3:font-semibold prose-h4:font-semibold prose-h5:font-semibold prose-h6:font-semibold",
)}
Expand Down
12 changes: 6 additions & 6 deletions src/app/light/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,25 +39,25 @@ export default function Layout({ children }: { children: React.ReactNode }) {
function ButtonPile() {
return (
<div className="group/pile relative pt-1.5">
<Button asChild className="group">
<Button asChild className="group gap-0! [&_svg]:shrink!">
<a
href="https://github.com/openstatusHQ/data-table-filters"
target="_blank"
rel="noreferrer"
>
<span className="mr-1">View GitHub Repo</span>
<ArrowRight className="relative mb-px inline h-4 w-0 transition-all group-hover:w-4" />
<ChevronRight className="relative mb-px inline h-4 w-4 transition-all group-hover:w-0" />
<ArrowRight className="relative mb-px inline h-4 w-0! transition-all group-hover:w-4!" />
<ChevronRight className="relative mb-px inline h-4 w-4! transition-all group-hover:w-0!" />
</a>
</Button>
<Button
asChild
className="group absolute top-0 -right-1.5 -z-10 opacity-70 transition-transform group-hover/pile:-translate-x-1.5 group-hover/pile:-translate-y-10 group-hover/pile:opacity-100"
className="group absolute top-0 -right-1.5 -z-10 gap-0! opacity-70 transition-transform group-hover/pile:-translate-x-1.5 group-hover/pile:-translate-y-10 group-hover/pile:opacity-100 [&_svg]:shrink!"
>
<a href="https://light.openstatus.dev" target="_blank" rel="noreferrer">
<span className="mr-1">Explore Light OS</span>
<ArrowRight className="relative mb-px inline h-4 w-0 transition-all group-hover:w-4" />
<ChevronRight className="relative mb-px inline h-4 w-4 transition-all group-hover:w-0" />
<ArrowRight className="relative mb-px inline h-4 w-0! transition-all group-hover:w-4!" />
<ChevronRight className="relative mb-px inline h-4 w-4! transition-all group-hover:w-0!" />
</a>
</Button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function DataTableFilterResetButton<TData>({
return (
<Button
variant="outline"
className="h-5 rounded-full px-1.5! py-1! font-mono text-[10px] shadow-none"
className="h-5 gap-1 rounded-full px-1.5! py-1! font-mono text-[10px] shadow-none"
onClick={(e) => {
e.stopPropagation();
column?.setFilterValue(undefined);
Expand Down
6 changes: 3 additions & 3 deletions src/components/layout/data-table-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ export function DataTableLayout({ children }: { children: React.ReactNode }) {
</Button>
{children}
<div className="fixed right-4 bottom-4 z-50">
<Button asChild className="group">
<Button asChild className="group gap-0! [&_svg]:shrink!">
<a
href="https://github.com/openstatusHQ/data-table-filters"
target="_blank"
rel="noreferrer"
>
<span className="mr-1">View GitHub Repo</span>
<ArrowRight className="relative mb-px inline h-4 w-0 transition-all group-hover:w-4" />
<ChevronRight className="relative mb-px inline h-4 w-4 transition-all group-hover:w-0" />
<ArrowRight className="relative mb-px inline h-4 w-0! transition-all group-hover:w-4!" />
<ChevronRight className="relative mb-px inline h-4 w-4! transition-all group-hover:w-0!" />
</a>
</Button>
</div>
Expand Down
2 changes: 2 additions & 0 deletions src/content/docs/00-introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ _**It’s not a library. It’s a playbook.**_

Build production-grade data tables for large datasets — with proven patterns you can copy into your codebase.

Install the [agent skill](/docs/quick-start#agent-skill) and let it handle the rest.

> This is a **work in progress** — our goal is to make it the fastest way to spin up a data-table view for logs and beyond. Questions, ideas, or feedback? [Open an issue on GitHub](https://github.com/openstatusHQ/data-table-filters/issues).

![data-table with some filters and activated live mode](/assets/docs/data-table.png)
Expand Down
24 changes: 12 additions & 12 deletions src/content/docs/01-quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,30 @@ faq:

# Quick Start

## Install

Start by installing the core `data-table` block — it includes the table engine, memory store adapter, and all 4 filter types:

```bash
npx shadcn@latest add https://data-table.openstatus.dev/r/data-table.json
```

Then add any extension block from the [registry](#blocks) below.

## Agent Skill

Install the skill and let your AI agent handle the setup:

```bash
npx skills add https://github.com/openstatushq/data-table-filters/skills
npx skills add https://github.com/openstatushq/data-table-filters --skill data-table-filters
```

Then prompt your agent:
Let the agent handle the rest:

> Add a data table with filters to my project

It detects your stack, installs the right blocks, and wires them up.

## Install Components

Start by installing the core `data-table` block — it includes the table engine, memory store adapter, and all 4 filter types:

```bash
npx shadcn@latest add https://data-table.openstatus.dev/r/data-table.json
```

Then add any extension block from the [registry](#blocks) below.

## Blocks

The following blocks can be installed via shadcn CLI.
Expand Down
9 changes: 8 additions & 1 deletion src/lib/mdx/mdx-remote.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Link } from "@/components/custom/link";
import { MDXRemote } from "next-mdx-remote/rsc";
import React from "react";
import rehypePrettyCode from "rehype-pretty-code";
Expand Down Expand Up @@ -47,14 +48,20 @@ const components = {
h4: createHeading(4),
h5: createHeading(5),
h6: createHeading(6),
a: Link,
table: Table,
img: CustomImage,
pre: Pre,
};

/** @type {import('rehype-pretty-code').Options} */
const prettyCodeOptions = {
const prettyCodeOptions: import("rehype-pretty-code").Options = {
keepBackground: false,
theme: {
light: "github-light-high-contrast",
dark: "github-dark-high-contrast",
},
defaultLang: "plaintext",
};

export function Mdx({ source }: { source: string }) {
Expand Down
18 changes: 18 additions & 0 deletions src/styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,24 @@ body {
@apply overflow-x-auto;
}

[data-rehype-pretty-code-figure] pre,
[data-rehype-pretty-code-figure] code,
[data-rehype-pretty-code-figure] code span {
--_light-fg: var(--shiki-light, inherit);
--_light-bg: var(--shiki-light-bg, transparent);
--_dark-fg: var(--shiki-dark, inherit);
--_dark-bg: var(--shiki-dark-bg, transparent);
color: var(--_light-fg);
background-color: var(--_light-bg);
}

.dark [data-rehype-pretty-code-figure] pre,
.dark [data-rehype-pretty-code-figure] code,
.dark [data-rehype-pretty-code-figure] code span {
color: var(--_dark-fg);
background-color: var(--_dark-bg);
}

.prose .table-wrapper {
@apply w-full overflow-x-auto;
}
Expand Down
Loading