marmelab/react-admin · error

<DeleteButton> components should be used inside a <Resource>

Error message

<DeleteButton> components should be used inside a <Resource> component or provided the resource prop.

What it means

DeleteButton deletes the current record of a resource; it needs both a record (from RecordContext or props) and a resource name (from ResourceContext or props). When the resource cannot be resolved, the delete mutation and access-control check cannot be built, so it throws.

Source

Thrown at packages/ra-ui-materialui/src/button/DeleteButton.tsx:70

 *     return <Edit actions={<EditActions />} {...props} />;
 * };
 */
export const DeleteButton = React.forwardRef(function DeleteButton<
    RecordType extends RaRecord = any,
>(
    inProps: DeleteButtonProps<RecordType>,
    ref: React.ForwardedRef<HTMLButtonElement>
) {
    const props = useThemeProps({
        name: PREFIX,
        props: inProps,
    });

    const { mutationMode, ...rest } = props;
    const record = useRecordContext(props);
    const resource = useResourceContext(props);
    if (!resource) {
        throw new Error(
            '<DeleteButton> components should be used inside a <Resource> component or provided the resource prop.'
        );
    }
    const { canAccess, isPending } = useCanAccess({
        action: 'delete',
        resource,
        record,
    });
    const saveContext = useSaveContext(props);
    if (!record || record.id == null || !canAccess || isPending) {
        return null;
    }

    const finalMutationMode = mutationMode
        ? mutationMode
        : saveContext?.mutationMode
          ? saveContext.mutationMode
          : 'undoable';

View on GitHub (pinned to 051f511bb0)

Solutions

  1. Pass resource (and record if no context): <DeleteButton resource="posts" record={post} />
  2. Wrap in <ResourceContextProvider> / <RecordContextProvider> or render inside Edit/Show/List views
  3. Forward resource from parent components when abstracting the button

Example fix

// before
export const MyActions = () => <DeleteButton />;

// after
export const MyActions = ({ resource, record }) => (
  <DeleteButton resource={resource} record={record} />
);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!resource || !record) return null; // DeleteButton needs both

Type guard

const canDelete = (p: { resource?: string; record?: Record }): p is { resource: string; record: Record } =>
  typeof p.resource === 'string' && p.resource.length > 0 && p.record != null;

Try / catch

try {
  return <DeleteButton {...props} />;
} catch (e) {
  if (e.message.includes('DeleteButton')) {
    return <DeleteButton resource={fallbackResource} record={fallbackRecord} {...props} />;
  }
  throw e;
}

Prevention

When it happens

Trigger: Rendering <DeleteButton /> outside a record/list context without resource (and typically record) props; using it in a custom row renderer that dropped the resource context; mounting it app-level for 'delete current item' logic.

Common situations: Custom action toolbars outside Edit/Show/List views; refactors moving DeleteButton into shared components; test setups rendering the button alone.

Related errors


AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30). Data as JSON: /api/errors/565eea92226dc450. Report an issue: GitHub.