Skip to content

1. List the authors

A library screen starts with a list. We have an AllAuthors query from the backend, generated into a typed proxy — so the first job is to get its results onto the screen in a table. And because that query is observable, the table will stay current on its own.

  1. Render the table. DataTableForObservableQuery takes the query proxy, subscribes to it, and renders each result as a row. You describe the columns with the Cratis-owned Column marker. Its field string must match a property on the generated Author read model:

    Authors.tsx
    import { Page } from '@cratis/components/Common';
    import { DataTableForObservableQuery } from '@cratis/components/DataTables';
    import { Column } from '@cratis/components/DataTables';
    import { AllAuthors } from './Authors/Author'; // generated observable query proxy
    export const Authors = () => (
    <Page title='Authors' panel>
    <DataTableForObservableQuery query={AllAuthors} emptyMessage='No authors yet'>
    <Column field='name' header='Name' sortable />
    </DataTableForObservableQuery>
    </Page>
    );

That’s the whole screen. The Page wrapper gives you the titled, panelled chrome the rest of the app uses; the table does the rest.

Notice everything that’s absent. There’s no fetch, no useEffect, no loading flag, no useState holding the rows, and — the part that matters most — no subscription code. DataTableForObservableQuery opened the observable for you. The moment something else in the app registers a new author, the backend pushes the new read-model value down, and this table re-renders with the extra row. You’ll see that happen for real in the next chapter, when we add the form.

The generated query and its row values are typed, but Column.field is currently a plain string and the compiler does not validate it against the read model. Keep field names aligned with the generated row type and update them when the backend property changes. For custom cells, use Column<Author> with a typed body callback so row-property access is compiler-checked. A misspelled raw field value is a runtime display/configuration error rather than a TypeScript error.

  • A library screen that lists every author in a table,
  • bound to the AllAuthors query with no subscription or fetch code,
  • that will update itself the instant the data behind it changes.

A read-only list isn’t a back office, though — a librarian needs to add authors. Next we’ll give the screen a command. Let’s act on the list →