Soft Delete Setup
The soft delete feature is an Enterprise Edition add-on that allows you to “delete” records without actually removing them from your database.
Use it to:
- Archive records safely instead of permanent deletion
- 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”npm install --save @react-admin/ra-core-ee# oryarn add @react-admin/ra-core-eeYou will need an active Enterprise Edition license to use this package. Please refer to the Enterprise Edition documentation for more details.
Data Provider
Section titled “Data Provider”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 }; },};Tip: ra-core-ee automatically populates the authorId parameter using authProvider.getIdentity() if it is implemented. It will use the id field of the returned identity object. Otherwise this field will be left blank.
Tip: Deleted records are immutable, so you don’t need to implement an updateDeleted method.
Once your provider has all soft-delete methods, pass it to the <CoreAdmin> component and you’re ready to start using the Soft Delete feature.
// in src/App.tsximport { CoreAdmin } from 'ra-core';import { dataProvider } from './dataProvider';
const App = () => <CoreAdmin dataProvider={dataProvider}>{/* ... */}</CoreAdmin>;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', } });Note: When using addSoftDeleteInPlace, avoid calling getListDeleted without a resource filter, as it uses a naive implementation combining multiple getList calls, which can lead to bad performance. It is recommended to use one list per resource in this case (see <DeletedRecordsListBase resource> property).
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.
Query and Mutation Hooks
Section titled “Query and Mutation Hooks”Each data provider verb has its own hook so you can use them in custom components:
softDelete:useSoftDeletesoftDeleteMany:useSoftDeleteManygetListDeleted:useGetListDeletedgetOneDeleted:useGetOneDeletedrestoreOne:useRestoreOnerestoreMany:useRestoreManyhardDelete:useHardDeletehardDeleteMany:useHardDeleteMany
createMany
Section titled “createMany”ra-core-ee provides a default implementation of the createMany method that simply calls create multiple times. However, some data providers may be able to create multiple records at once, which can greatly improve performances.
const dataProviderWithCreateMany = { ...dataProvider, createMany: (resource, params: CreateManyParams): CreateManyResult => { const {data} = params; // data is an array of records. // ... return {data: createdRecords}; },};