React-admin is a comprehensive frontend framework for building B2B and admin applications on top of REST/GraphQL APIs, using TypeScript, React, and Material UI. It's an open-source project maintained by Marmelab that provides a complete solution for B2B applications with data-driven interfaces.
- Designed for Single-Page Application (SPA) architecture
- Provider-based abstraction for data fetching, auth, and i18n
- "Batteries included but removable" - everything is replaceable
- User Experience and Developer Experience are equally important
- Backward compatibility prioritized over new features
- Composition over Configuration - Use React patterns, not custom DSLs
- Minimal API Surface - If it can be done in React, don't add to core
- Standing on Giants' Shoulders - Use best-in-class libraries, don't reinvent the wheel
React-admin uses adapters called "providers" for external integrations:
// Data Provider - abstracts API calls
dataProvider.getList('posts', {
pagination: { page: 1, perPage: 5 },
sort: { field: 'title', order: 'ASC' },
filter: { author_id: 12 }
})
// Auth Provider - handles authentication
authProvider.checkAuth()
authProvider.login({ username, password })
authProvider.getPermissions()
// i18n Provider - manages translations
i18nProvider.translate('ra.action.save')All functionality exposed through hooks following React patterns:
// Data hooks
const { data, isLoading } = useGetList('posts', {
pagination: { page: 1, perPage: 10 }
});
// State management hooks
const [filters, setFilters] = useFilterState();
const [page, setPage] = usePaginationState();
// Auth hooks
const { permissions } = usePermissions();
const canAccess = useCanAccess({ resource: 'posts', action: 'edit' });The ra-core package contains all logic without UI. UI components are in separate packages like ra-ui-materialui. This allows:
- Custom UIs using core hooks and controllers
- Swapping UI libraries without changing core logic
- Controllers in
ra-core/src/controller/handle business logic - Views in
ra-ui-materialui/src/handle rendering - Controllers provide data and callbacks via hooks
Communication between components can be challenging, especially in large React applications, where passing props down several levels can become cumbersome. React-admin addresses this issue using a pull model, where components expose props to their descendants via a context, and descendants can consume these props using custom hooks.
Whenever a react-admin component fetches data or defines a callback, it creates a context and places the data and callback in it.
react-admin/
├── packages/ # Lerna-managed packages
│ ├── ra-core/ # Core logic, hooks, controllers
│ ├── ra-ui-materialui/ # Material UI components
│ ├── react-admin/ # Main distribution package
│ ├── ra-data-*/ # Data provider adapters
│ ├── ra-i18n-*/ # i18n providers
│ └── ra-language-*/ # Translation packages
├── examples/ # Example applications
│ ├── simple/ # E2E test app
│ ├── demo/ # Full e-commerce demo
│ ├── crm/ # CRM application
│ └── tutorial/ # Tutorial app
├── cypress/ # E2E test configuration
├── docs/ # Jekyll documentation
├── docs_headless/ # Astro + Starlight documentation for headless components
└── scripts/ # Build scripts
src/auth/- Authentication and authorization (54 files)src/controller/- CRUD controllers and state managementsrc/dataProvider/- Data fetching and caching logic (70 files)src/form/- Form handling (31 files)src/routing/- Navigation and routing (26 files)src/i18n/- Internationalization (30 files)
- Core: React 18.3+, TypeScript 5.8+, lodash 4.17+, inflection 3.0+
- Routing: React Router 6.28+
- Data: TanStack Query 5.90+ (React Query)
- Forms: React Hook Form 7.53+
- UI Components: Material UI 5.16+
- Testing: Jest 29.5+, Testing Library, Storybook, Cypress
- Strict mode enabled - no implicit any
- Complete type exports - all public APIs must be typed
- Generic types for flexibility in data providers and resources
- JSDoc comments for better IDE support
// GOOD - Properly typed with generics
export const useGetOne = <RecordType extends RaRecord = any>(
resource: string,
options?: UseGetOneOptions<RecordType>
): UseGetOneResult<RecordType> => { ... }
// BAD - Using any without constraint
export const useGetOne = (resource: any, options?: any): any => { ... }- Composition over configuration - Use React composition patterns
- Smart defaults - Components should work out-of-the-box
- Controlled and uncontrolled modes supported
- Props pass-through - Spread additional props to root element
// Component composition example
export const MyField = ({ source, ...props }) => {
const record = useRecordContext();
return (
<TextField
{...props} // Pass through all additional props
value={record?.[source]}
/>
);
};- Feature-based structure within packages (not type-based)
- Co-location - Tests (
.spec.tsx) and stories (.stories.tsx) next to components - Index exports - Each directory has an index.ts exporting public API
- Flat structure within features - avoid unnecessary nesting
Every new feature or API change must be documented. The documentation consists of Markdown files located in the /docs/ directory and built with Jekyll, one file per component or hook.
All documentation files must include:
- A brief description of the component or hook
- Usage examples
- List of props or parameters (required props first, then in alphabetical order)
- Detailed usage for each prop/parameter (in alphabetical order)
- Recipes and advanced usage examples if applicable
Headless hooks and components (the ones in ra-core) are also documented in the /docs_headless/ directory.
- Automatic test execution for modified files
- Prettier formatting check
- ESLint validation
- TypeScript compilation
# Initial setup
make install # Install all dependencies
# After making changes
make build # Build packages (TypeScript compilation)
make test # run unit and e2e tests
# Before pushing changes
make lint # Check code quality
make prettier # Format code-
Target branch:
nextfor features,masterfor bug fixes or documentation changes -
Required checks:
- All tests passing (
make test) - Linting clean (
make lint) - Prettier formatted (
make prettier) - TypeScript compiles (
yarn typecheck)
- All tests passing (
-
Commit Messages: Clear, descriptive messages focusing on "why"
fix: Prevent duplicate API calls in useGetList hook feat: Add support for custom row actions in Datagrid docs: Clarify dataProvider response format -
Documentation: Update relevant docs for API changes
-
Title: Start with a verb (Add / Fix / Update / Remove), prefix with
[Doc]or[TypeScript]if the change only concerns doc or types.
make # Show all available commands
make install # Install dependencies
make build # Build all packages (CJS + ESM)
make test # Run all tests
make lint # Check code quality
make prettier # Format code
make run-simple # Run simple example
make run-demo # Run demo application- Use
React.memo()for expensive components - Leverage
useMemo()anduseCallback()appropriately - Use
useEvent()(an internal hook) for memoized event handlers - Implement pagination for large datasets
- Use query caching via React Query
- Follow WCAG guidelines
- Ensure keyboard navigation works
- Provide proper ARIA labels
- Test with screen readers
- Modern browsers only (Chrome, Firefox, Safari, Edge)
- No IE11 support
- ES5 compilation target for compatibility
- Don't use
React.cloneElement()- it breaks composition - Don't inspect children - violates React patterns (exception: Datagrid)
- Don't add comments when code is self-explanatory
- Don't add features achievable in a few lines with pure React
- Don't skip tests - they run automatically on commit
- Don't force push to main/master branches
All developments must include tests to ensure code quality and prevent regressions.
- Location: Stories alongside components as
*.stories.tsx - Coverage: All components must have stories demonstrating usage for all props
- Mocking: Use jest mocks sparingly, prefer integration tests
- Data: Use mock data providers (e.g., FakeRest) for stories. Make realistic data scenarios as the stories are also used for screenshots and visual testing.
- Location: Tests alongside source files as
*.spec.tsx - Test Cases: Reuse the component's stories as test cases
- Assertions: Use testing-library to render and assert on elements. Don't test implementation details or HTML attributes, use assertions based on user interactions and visible output.
- Commands:
make test-unit # Run all unit and functional tests npx jest [pattern] # Run specific tests
Kept minimal to critical user paths due to maintenance overhead.
-
Location:
cypress/directory -
Target: Simple example app (
examples/simple/) -
Coverage: Critical user paths and interactions
-
Commands:
make test-e2e # Run in CI mode # or for local testing with GUI: make run-simple # Start test app with vite make test-e2e-local # Run e2e tests with GUI
make lint # ESLint checks
make prettier # Prettier formatting
make build # TypeScript type checking