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
useShowControllerto fetch the record from the dataProvider viadataProvider.getOne(), - it computes the default page title
- it creates a
ShowContextand aRecordContext, - 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
| Prop | Required | Type | Default | Description |
|---|---|---|---|---|
children | Optional * | ReactNode | The components rendering the record fields | |
render | Optional * | (showContext) => ReactNode | A function rendering the record fields, receive the show context as its argument | |
actions | Optional | ReactElement | The actions to display in the toolbar | |
aside | Optional | ReactNode | Side panel rendered alongside the record content | |
component | Optional | ElementType | "div" | Override the root element wrapping the record content |
className | Optional | string | passed to the root component | |
error | Optional | ReactNode | default msg | Content to display when a data-fetch error occurs |
disableAuthentication | Optional | boolean | Set to true to disable the authentication check | |
disableBreadcrumb | Optional | boolean | false | Set to true to define a custom breadcrumb for the page, instead of the default one |
emptyWhileLoading | Optional | boolean | Set to true to return null while the show is loading | |
offline | Optional | ReactNode | default msg | Content to display when the browser is offline and the record is still loading |
id | Optional | string | number | The record id. If not provided, it will be deduced from the URL | |
queryOptions | Optional | object | The options to pass to the useQuery hook | |
resource | Optional | string | The resource name, e.g. posts | |
title | Optional | string | ReactElement | false | The 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}]},
}
}