Soft Delete
Shadcn Admin Kit provides hooks and components to let users “delete” records without actually removing them from your database.
Use it to:
- Archive records safely instead of permanent deletion
- Require a second confirmation step before permanent deletion (“four-eyes” principle)
- Browse and filter all deleted records in a dedicated interface
- Restore archived items individually or in bulk
- Track who deleted what and when
Installation
Section titled “Installation”The soft delete features require a valid Enterprise Edition subscription. Once subscribed, follow the instructions to get access to the private npm repository.
You can then install the npm package providing the realtime features using your favorite package manager:
npm install --save @react-admin/ra-core-ee# oryarn add @react-admin/ra-core-eeFeatures
Section titled “Features”The Soft Delete packages let you build a complete soft delete experience in your admin, including:
<SoftDeleteButton>: A button that marks the current record as deleted instead of permanently deleting it.<BulkSoftDeleteButton>: A button that marks the selected records as deleted instead of permanently deleting them.<RestoreButton>: A button to restore a soft deleted record.<DeletedRecordsListBase>: A Deleted Records list view to browse and filter deleted records.<ShowDeletedBase>: A Deleted Record show view to see the details of a deleted record.<DeletedRecordRepresentation>: A component that renders the record representation of a deleted record.
It leverages the following hooks to interact with the data provider:
useSoftDeleteuseSoftDeleteManyuseGetListDeleteduseGetOneDeleteduseRestoreOneuseRestoreManyuseHardDeleteuseHardDeleteManyuseDeleteRecordsListController
Data Provider Requirements
Section titled “Data Provider Requirements”In order to use the Soft Delete features, your data provider must implement a few new methods.
Methods
Section titled “Methods”The Soft Delete features of ra-core-ee rely on the dataProvider to soft-delete, restore or view deleted records.
In order to use those features, you must add a few new methods to your data provider:
softDeleteperforms the soft deletion of the provided record.softDeleteManyperforms the soft deletion of the provided records.getOneDeletedgets one deleted record by its ID.getListDeletedgets a list of deleted records with filters and sort.restoreOnerestores a deleted record.restoreManyrestores deleted records.hardDeletepermanently deletes a record.hardDeleteManypermanently deletes many records.- (OPTIONAL)
createManycreates multiple records at once. This method is used internally by some data provider implementations to delete or restore multiple records at once. As it is optional, a default implementation is provided that simply callscreatemultiple times.
Signature
Section titled “Signature”Here is the full SoftDeleteDataProvider interface:
const dataProviderWithSoftDelete: SoftDeleteDataProvider = { ...dataProvider,
softDelete: (resource, params: SoftDeleteParams): SoftDeleteResult => { const { id, authorId } = params; // ... return { data: deletedRecord }; }, softDeleteMany: ( resource, params: SoftDeleteManyParams, ): SoftDeleteManyResult => { const { ids, authorId } = params; // ... return { data: deletedRecords }; },
getOneDeleted: (params: GetOneDeletedParams): GetOneDeletedResult => { const { id } = params; // ... return { data: deletedRecord }; }, getListDeleted: (params: GetListDeletedParams): GetListDeletedResult => { const { filter, sort, pagination } = params; // ... return { data: deletedRecords, total: deletedRecords.length }; },
restoreOne: (params: RestoreOneParams): RestoreOneResult => { const { id } = params; // ... return { data: deletedRecord }; }, restoreMany: (params: RestoreManyParams): RestoreManyResult => { const { ids } = params; // ... return { data: deletedRecords }; },
hardDelete: (params: HardDeleteParams): HardDeleteResult => { const { id } = params; // ... return { data: deletedRecordId }; }, hardDeleteMany: (params: HardDeleteManyParams): HardDeleteManyResult => { const { ids } = params; // ... return { data: deletedRecordsIds }; },};Once your provider has all soft-delete methods, pass it to the <Admin> component and you’re ready to start using the Soft Delete feature.
// in src/App.tsximport { Admin } from '@/components/admin/admin';import { dataProvider } from './dataProvider';
const App = () => <Admin dataProvider={dataProvider}>{/* ... */}</Admin>;Deleted Record Structure
Section titled “Deleted Record Structure”A deleted record is an object with the following properties:
id: The identifier of the deleted record.resource: The resource name of the deleted record.deleted_at: The date and time when the record was deleted, in ISO 8601 format.deleted_by: (optional) The identifier of the user who deleted the record.data: The original record data before deletion.
Here is an example of a deleted record:
{ id: 123, resource: "products", deleted_at: "2025-06-06T15:32:22Z", deleted_by: "johndoe", data: { id: 456, title: "Lorem ipsum", teaser: "Lorem ipsum dolor sit amet", body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', },}Builders
Section titled “Builders”ra-core-ee comes with two built-in implementations that will add soft delete capabilities to your data provider without any specific backend requirements. You can choose the one that best fits your needs:
-
addSoftDeleteBasedOnResourcestores the deleted records for all resources in a single resource. This resource is nameddeleted_recordsby default.With this builder, all deleted records disappear from their original resource when soft-deleted, and are recreated in the
deleted_recordsresource.
// in src/dataProvider.tsimport { addSoftDeleteBasedOnResource } from '@react-admin/ra-core-ee';import baseDataProvider from './baseDataProvider';
export const dataProvider = addSoftDeleteBasedOnResource(baseDataProvider, { deletedRecordsResourceName: 'deleted_records',});-
addSoftDeleteInPlacekeeps the deleted records in the same resource, but marks them as deleted.With this builder, all deleted records remain in their original resource when soft-deleted, but are marked with the
deleted_atanddeleted_byfields. The query methods (getList,getOne, etc.) automatically filter out deleted records.You’ll need to pass a configuration object with all soft deletable resources as key so that
getListDeletedknows where to look for deleted records.
// in src/dataProvider.tsimport { addSoftDeleteInPlace } from '@react-admin/ra-core-ee';import baseDataProvider from './baseDataProvider';
export const dataProvider = addSoftDeleteInPlace(baseDataProvider, { posts: {}, comments: { deletedAtFieldName: 'deletion_date', }, accounts: { deletedAtFieldName: 'disabled_at', deletedByFieldName: 'disabled_by', },});You can also write your own implementation. Feel free to look at these builders source code for inspiration. You can find it under your node_modules folder, e.g. at node_modules/@react-admin/ra-core-ee/src/soft-delete/dataProvider/addSoftDeleteBasedOnResource.ts.
createMany
Section titled “createMany”The <BulkSoftDeleteButton> has to create or update several records at once. If your data provider supports the createMany method to create multiple records at once, it will use it instead of calling create multiple times for performance reasons.
const dataProviderWithCreateMany = { ...dataProvider, createMany: (resource, params: CreateManyParams): CreateManyResult => { const { data } = params; // data is an array of records. // ... return { data: createdRecords }; },};