Show

The <Show> component is a page component that renders a single record.

Installation

pnpm dlx shadcn@latest add @shadmin/show

<Show> handles the logic of the Show page:

  • it calls useShowController to fetch the record from the dataProvider via dataProvider.getOne(),
  • it computes the default page title
  • it creates a ShowContext and a RecordContext,
  • it renders the page layout with the correct title and actions
  • it renders its child component in a <Card>

Usage

Here is the minimal code necessary to display a view to show a post:

// in src/products.jsx
import { RecordField } from "@/components/admin/record-field";
import { NumberField } from "@/components/admin";
import { ReferenceField } from "@/components/admin/reference-field";
import { Show } from "@/components/admin/show";
 
export const ProductShow = () => (
  <Show>
    <div className="flex flex-col gap-4">
      <RecordField source="reference" />
      <RecordField label="dimensions">
        <div className="flex items-center gap-1">
<NumberField source="width" />
<NumberField source="height" />
        </div>
      </RecordField>
      <RecordField source="category_id">
        <ReferenceField source="category_id" reference="categories" />
      </RecordField>
      <RecordField
        source="price"
        render={(record) => Intl.NumberFormat().format(record.price)}
      />
      <RecordField
        source="thumbnail"
        render={(record) => <img src={record.thumbnail} className="w-24" />}
      />
      <RecordField source="description" className="max-w-100" />
    </div>
  </Show>
);

<RecordField> is a flexible wrapper to display a label and a value (field component, render function, or children) with optional layout variants. See RecordField documentation for details.

Components using <Show> can be used as the show prop of a <Resource> component:

// in src/App.jsx
import { Admin } from "@/copmponents/admin";
import { Resource } from "ra-core";
 
import { dataProvider } from "./data-provider";
import { ProductShow } from "./products";
 
const App = () => (
  <Admin dataProvider={dataProvider}>
    <Resource name="products" show={ProductShow} />
  </Admin>
);

That's enough to display the post show view above.

Scaffolding A Show Page

You can use <ShowGuesser> to quickly bootstrap a Show view on top of an existing API, without adding the fields one by one.

// in src/App.js
import { Admin, ShowGuesser } from "@/components/admin";
import { Resource } from "ra-core";
import { dataProvider } from "./data-provider";
 
const App = () => (
  <Admin dataProvider={dataProvider}>
    {/* ... */}
    <Resource name="products" show={ShowGuesser} />
  </Admin>
);

Just like <Show>, <ShowGuesser> fetches the data. It then analyzes the response, and guesses the fields it should use to display a basic layout with the data. It also dumps the components it has guessed in the console, so you can copy it into your own code.

Props

PropRequiredTypeDefaultDescription
childrenOptional *ReactNodeThe components rendering the record fields
renderOptional *(showContext) => ReactNodeA function rendering the record fields, receive the show context as its argument
actionsOptionalReactElementThe actions to display in the toolbar
asideOptionalReactNodeSide panel rendered alongside the record content
componentOptionalElementType"div"Override the root element wrapping the record content
classNameOptionalstringpassed to the root component
errorOptionalReactNodedefault msgContent to display when a data-fetch error occurs
disableAuthenticationOptionalbooleanSet to true to disable the authentication check
disableBreadcrumbOptionalbooleanfalseSet to true to define a custom breadcrumb for the page, instead of the default one
emptyWhileLoadingOptionalbooleanSet to true to return null while the show is loading
offlineOptionalReactNodedefault msgContent to display when the browser is offline and the record is still loading
idOptionalstring | numberThe record id. If not provided, it will be deduced from the URL
queryOptionsOptionalobjectThe options to pass to the useQuery hook
resourceOptionalstringThe resource name, e.g. posts
titleOptionalstring | ReactElement | falseThe title to display in the App Bar

* You must provide either children or render.

render

An alternative to children. Pass a function receiving the full ShowControllerResult and returning a React node:

import { Show } from "@/components/admin";
 
export const PostShow = () => (
  <Show
    render={({ record, isPending }) =>
      isPending ? (
        <p>Loading…</p>
      ) : (
        <div>
          <h1>{record?.title}</h1>
          <p>{record?.body}</p>
        </div>
      )
    }
  />
);

When render is provided, children is ignored.

offline

When the browser goes offline while the record is still loading (isPaused && isPending), <Show> renders a default "Offline" message. Override it with any ReactNode:

import { Show, SimpleShowLayout, RecordField } from "@/components/admin";
 
export const PostShow = () => (
  <Show
    offline={
      <p className="text-muted-foreground p-4">You appear to be offline.</p>
    }
  >
    <SimpleShowLayout>
      <RecordField source="title" />
    </SimpleShowLayout>
  </Show>
);

Set offline={false} to suppress the offline state entirely.

error

When dataProvider.getOne() fails, <Show> renders a default error message. Override it with any ReactNode:

import { Show, SimpleShowLayout, RecordField } from "@/components/admin";
 
export const PostShow = () => (
  <Show
    error={<p className="text-destructive p-4">Could not load this record.</p>}
  >
    <SimpleShowLayout>
      <RecordField source="title" />
    </SimpleShowLayout>
  </Show>
);

Set error={false} to suppress the error message entirely.

component

By default, <Show> wraps the record content in a <div>. Pass any React element type to component to replace it:

import { Show, SimpleShowLayout, RecordField } from "@/components/admin";
import { Card } from "@/components/ui/card";
 
export const PostShow = () => (
  <Show component={Card}>
    <SimpleShowLayout>
      <RecordField source="title" />
    </SimpleShowLayout>
  </Show>
);

aside

Pass any React node as aside to render a side panel next to the record content. The aside floats to the right in a flex row — the content takes flex-1 and the aside gets a fixed w-64 width.

import { Show, SimpleShowLayout, RecordField } from "@/components/admin";
 
const ShowNotes = () => (
  <div className="p-4 bg-muted rounded text-sm">
    <p className="font-medium mb-2">Related actions</p>
    <p>Use the Edit button in the toolbar to modify this record.</p>
  </div>
);
 
export const PostShow = () => (
  <Show aside={<ShowNotes />}>
    <SimpleShowLayout>
      <RecordField source="title" />
      <RecordField source="body" />
    </SimpleShowLayout>
  </Show>
);

Live Updates

Shadmin offers Realtime features to automatically refresh the data on screen when it has been changed by another user.

If you want to subscribe to live updates the record, you can rely on the useSubscribeToRecord hook.

A sample use case would be to refresh the <Show> view when the record has been updated by another user.

First, create a <RecordLiveUpdate> component that uses the useSubscribeToRecord hook to subscribe to updates on the current record. When an update is received, it calls the refetch function from the ShowContext to refresh the record.

// src/components/admin/record-live-update.tsx
 
import { useSubscribeToRecord } from "@react-admin/ra-core-ee";
import { Identifier, useShowContext } from "ra-core";
import { useCallback } from "react";
 
export const RecordLiveUpdate = (props: RecordLiveUpdateProps) => {
  const { refetch } = useShowContext();
  const handleUpdate = useCallback(() => {
    refetch();
  }, [refetch]);
 
  useSubscribeToRecord(handleUpdate, props.resource, props.id);
 
  return null;
};
 
type RecordLiveUpdateProps = {
  resource?: string;
  id?: Identifier;
};

Then, add the <RecordLiveUpdate> in your <Show> children:

import { TextField } from "@/components/admin/data-table";
import { RecordLiveUpdate } from "@/components/admin/record-live-update";
import { Show } from "@/components/admin/show";
 
const PostList = () => (
  <Show>
    <TextField source="title" />
    <RecordLiveUpdate />
  </Show>
);

To trigger refreshes of <RecordLiveUpdate>, the API has to publish events containing at least the followings:

{
    topic : '/resource/{resource}/{id}',
    event: {
        type: 'updated',
        payload: { ids: [{listOfRecordIdentifiers}]},
    }
}