react-resource-view

Introduction

One declaration, five views — list, detail, create, edit, delete — and a URL that carries the state.

react-resource-view renders the CRUD surface of a resource: the list and its layouts, the detail page, the create and edit forms, the delete confirmation, the filter bar and the pagination. You describe the resource; it writes the screens. Those are its five views — list, detail, create, edit and delete — and the list alone comes in seven layouts.

It is built on react-data-form for everything with fields in it, which is why the two are documented side by side.

One declaration#

articles.ts
import {
  DatePickerInputController,
  NumberInputController,
  SelectInputController,
} from "react-data-form"
import { createViewResource, layout } from "react-resource-view"

const CATEGORIES = [
  { label: "Forms", value: "Forms" },
  { label: "Views", value: "Views" },
  { label: "Routing", value: "Routing" },
  { label: "JSON-LD", value: "JSON-LD" },
  { label: "Architecture", value: "Architecture" },
]

const STATUSES = [
  { label: "Draft", value: "draft" },
  { label: "In review", value: "review" },
  { label: "Published", value: "published" },
]

export const articles = createViewResource("articles", {
  name: "Articles",
  path: "/api/articles",
  // The fields are the form *and* the table's columns.
  form: {
    inputs: {
      title: { label: "Title", required: true },
      author: { label: "Author" },
      category: {
        label: "Category",
        controller: SelectInputController,
        valueOptions: CATEGORIES,
      },
      status: {
        label: "Status",
        controller: SelectInputController,
        valueOptions: STATUSES,
      },
      readingTime: { label: "Minutes", controller: NumberInputController },
      publishedAt: { label: "Published on", controller: DatePickerInputController },
    },
  },
  views: {
    list: {
      filters: {
        inputs: {
          title: { label: "Search a title" },
          status: {
            label: "Status",
            controller: SelectInputController,
            valueOptions: STATUSES,
          },
        },
      },
      layouts: [layout.table(), layout.cards({ columns: 3 })],
    },
  },
})

Rendering it is one component:

import { ViewResourceContextProvider } from "react-resource-view"
import { ActionList } from "react-data-form"

<ViewResourceContextProvider
  resource={articles}
  resourceAction={ActionList.list}
/>

What that gives you#

Everything below comes from a declaration of that shape — no screen was written by hand. Switch the layout, filter the list, open a row, edit it, delete it. Where those forms open is one key — a page, a dialog or a drawer — and what they ask for need not be the same when creating as when editing, which is a form per action. A layout the package does not ship is one command away — create your own layout.

A resource, running

No server behind this page

A resource declared without a path falls back to a localStorage-backed repository. Every demo on this site runs against a real repository with no API behind it — your edits survive a reload.

The URL is the state#

Which view, which item, which filters, which page, which layout: all of it lives in the address bar rather than in component state.

  • A link reopens exactly what the sender was looking at.
  • The back button does what the reader expects.
  • A reload does not lose the filters — which is the difference between a list people use and a list people fight.

Two routing modes carry it: in the path, or entirely in the query string for statically hosted or embedded views.

What it does not do#

  • It has no router. Four primitives are asked for; an adapter for TanStack Router ships with the package.
  • It knows no API in particular. Which backend answers, and how it spells a page or a filter, is a dialect — API Platform, Spring Boot, Supabase, Strapi, FastAPI and Django ship with the package, and any other REST API works without one — configured separately through configureApi.
  • It is not an admin panel. The library itself is a set of components you render where you want them, with no generated dashboard. The starter command writes a back office on your API for you, and AdminLayout is an optional shell for it — neither is required.
  • It does not own your layout. A view is a component, not a page.

Where to go next#

Other projects#

  • React Data Form — the form library this package renders its fields with, documented on this same site and published separately as react-data-form.
  • cardona.digital — the portfolio of Salvador Cardona, who writes and maintains both packages.