The back office of your REST API, generated from a declaration

Lists, detail pages, forms and filters for every resource — declared once, never drawn by hand.

Before and after

Hundreds of lines of components, or one declaration

A back office is the same work for every resource of your API. Write it by hand and each one costs a table, a form and its filters; declare it and each one costs about twenty lines.

By hand, per resource

  • A table component: columns, sorting, pagination
  • A form component per screen, create and edit
  • Filters wired to the query string
  • Validation, and the API's 422 mapped back onto the fields
  • Routes, permissions, loading and empty states — for every resource

Typically a few hundred lines.

With react-resource-view

resources/articles.ts
import { createViewResource, layout } from "react-resource-view"

export const articles = createViewResource<Article>("articles", {
  name: "Articles",
  path: "/api/articles",
  form: {
    inputs: {
      title: { label: "Title", required: true },
      author: { label: "Author" },
      status: { label: "Status", controller: SelectInputController },
    },
  },
  views: {
    list: {
      filters: { inputs: { title: { label: "Search a title" } } },
      layouts: [layout.table(), layout.cards()],
    },
  },
})

Is it for you?

When to use it, and when not to

Use it for

  • A back office or an admin interface for your API
  • An internal tool
  • Business CRUD: records to list, filter, read and edit

Look elsewhere for

  • A marketing or showcase site
  • A very custom UI where every screen is designed one by one

Written once

One description, every screen

A list has no separate column definition. The fields you describe are the form the reader edits, the columns the table shows and the payload your API receives — so take the description below apart and watch both halves follow.

Live — edit the description

Fields · 4 columns

  • Titletext
  • Authortext
  • Categoryone of five
  • Statusdraft · review · published
  • Minutesnumber
  • Published ondate

The table has no column list of its own. Every switch above moves the form and the list at the same time — because they read the same object.

Read many ways

Seven layouts, one resource

A layout is a line in views.list.layouts, not a screen you build. Declare several and the reader picks — the filters, the permissions and the forms carry over untouched.

The eighth layout is yours

Create your own layout: one file — three components and a layout.custom() call. One command writes it where you want it.

$ npx react-resource-view create-view-variant
Read the guide

Forms + views

Put the two together and you get a page builder

A page builder is not a third package. The blocks you stack and edit are a react-data-form form, saved as plain JSON; once published, the page is a record of a react-resource-view resource — listed, filtered and reopened in the back office like any other. The same pairing gives you a CV editor, a product sheet or a blog.

Open the page builder yourself

Opinionated about description, agnostic about everything else

The URL is the state

Filters, pagination, the chosen layout and the open item all live in the address bar. A shared link reopens exactly what the sender was looking at.

Ports, not assumptions

Neither package knows your router, your API client or your brand. Each touch point is a port with a default, injected once at startup.

Five backends, plus any REST API

A dialect holds what each backend spells differently — pages, filters, envelopes, errors — so one declared resource renders against API Platform, Spring Boot, Supabase, Strapi, FastAPI or Django, any REST API that publishes OpenAPI, or a dialect you write.

Validation on both sides

A validator per field for the browser, and a mapper that puts a 422 from the server back on the fields that caused it.

One dictionary

Labels go through react-mini-i18n, so your application and the libraries translate from the same place.

Seven layouts, and yours

Table, cards, item list, columns, split, calendar and timeline — declared side by side, switched by the reader. An eighth of your own is one command and one file.

Resource, dialect, port, scope — the concepts and how they fit together

Where it comes from

Build your application the way you build a Notion project

I moved from Evernote to Notion in 2018, and missed it in every app I wrote.

  1. 2018

    In Notion

    One set of records: a table, a board or a calendar, a click apart.

  2. 2018 – 2022

    In a React app

    The view is frozen in the code: another view means another screen.

  3. 2022 → today

    With react-resource-view

    Describe the resource once. The reader picks the view, and the link keeps it.

Declare a resource, and keep the forms for later

The quickstart goes from zero to a list on your API in five minutes. The form package it builds on stands on its own, when all you need is a form.

Both are written and maintained by Salvador Cardona — the rest of his work is on the author’s portfolio.