Cross-Platform¶
Craft Easy Admin runs on web, iOS, and Android from a single codebase using React Native + Expo. The UI adapts automatically to each platform.
Platform Support¶
| Platform | Runtime | Notes |
|---|---|---|
| Web | react-native-web | Standard browser deployment |
| iOS | Expo (native) | Full native performance |
| Android | Expo (native) | Full native performance |
Responsive Layout¶
Desktop (width >= 768px)¶
- Sidebar is permanently visible on the left (250px wide)
- Header spans the remaining width, no menu button
- Content fills the remaining space
┌──────────┬─────────────────────────────────┐
│ │ Header │
│ Sidebar ├─────────────────────────────────┤
│ (250px) │ │
│ │ Content Area │
│ │ │
│ │ │
└──────────┴─────────────────────────────────┘
Mobile (width < 768px)¶
- Sidebar hidden, rendered as a drawer/modal overlay
- Header shows a menu button to open the drawer
- Content takes full width
┌─────────────────────────────────┐
│ [☰] Header │
├─────────────────────────────────┤
│ │
│ Content Area │
│ (full width) │
│ │
└─────────────────────────────────┘
Mobile-Optimized Components¶
Several components use modal-based pickers optimized for touch:
| Component | Desktop | Mobile |
|---|---|---|
| Select | Modal dropdown | Slide-up modal with FlatList |
| MultiSelect | Modal with checkboxes | Same — touch-friendly checkboxes |
| RelationPicker | Search modal | Same — with ActivityIndicator |
| Navigation | Sidebar | Drawer overlay |
All interactive elements use appropriate touch targets:
- Minimum
hitSlop={8}on small buttons Pressablecomponents with adequate spacing- Modal animations via
animationType="slide"for native feel
ETag Collision Modal¶
When two users edit the same document simultaneously, the API returns 412 Precondition Failed. The admin app handles this with a dedicated collision modal:
interface ETagCollisionModalProps {
visible: boolean;
onReload: () => void;
onOverwrite: () => void;
onCancel: () => void;
}
When it appears: A PATCH or PUT request fails with HTTP 412, meaning the document was modified since it was loaded.
Options:
| Action | Behavior |
|---|---|
| Reload | Fetches the latest version, re-initializes the form with updated data |
| Overwrite | Fetches the latest ETag, re-submits the user's changes with the new ETag |
| Cancel | Closes the modal, abandons the save attempt |
This prevents silent data loss from concurrent edits.
Soft Delete Filter¶
List views include a SoftDeleteFilter toggle that controls whether soft-deleted items are visible:
When enabled, the list query includes is_deleted=true items in the results.
Views¶
The admin app includes four view types that adapt to the current resource schema:
ListView¶
- Breadcrumb navigation
- Title with item count
- Search bar (when
search_fieldsconfigured) - Sort chips (when
sort_fieldsconfigured) - Paginated list with
FlatList - Bulk selection via long-press
- Create button (when
features.createis true)
DetailView¶
- Read-only display of all fields
- Edit button (when
features.editis true) - Delete button with confirmation dialog (when
features.deleteis true) - Audit trail section (created_at, updated_at, created_by, updated_by)
- Formatted values: booleans as checkmarks, dates localized, numbers formatted
CreateView¶
- Form groups from
resource.form.groups - Field validation: required, min_length, regex pattern
- Cancel and Save buttons
EditView¶
- Same form layout as CreateView
- Pre-populated with current data via
useResource() - ETag collision detection on save
- Cancel, Save buttons
npm Package¶
@craft-easy/admin is published to npm and can be used as a library when embedding the admin framework into an existing Expo project.
Installation¶
Peer Dependencies¶
The package requires the following peer dependencies to be installed in the host project:
| Package | Version |
|---|---|
react |
>= 18.0.0 |
react-native |
>= 0.74.0 |
expo |
>= 55.0.0 |
expo-router |
>= 5.0.0 |
expo-constants |
>= 17.0.0 |
expo-linking |
>= 7.0.0 |
expo-status-bar |
>= 2.0.0 |
react-native-safe-area-context |
>= 5.0.0 |
react-native-screens |
>= 4.0.0 |
Web-only (optional):
| Package | Version |
|---|---|
react-dom |
>= 18.0.0 |
react-native-web |
>= 0.19.0 |
Exported Components¶
All exports are available from the root @craft-easy/admin entry point, or via subpath imports for tree-shaking.
View Components (@craft-easy/admin/components)¶
| Export | Description |
|---|---|
ListView |
Paginated resource list with search, sort chips, and bulk selection |
DetailView |
Read-only record display with audit trail |
CreateView |
Form view for creating new records |
EditView |
Pre-populated form view with ETag collision detection |
Layout Components (@craft-easy/admin/components)¶
| Export | Description |
|---|---|
Breadcrumbs |
Navigation breadcrumb bar |
Header |
App header with menu button for mobile |
Sidebar |
Schema-driven navigation sidebar |
ApplicationSwitcher |
Switcher for multi-API sessions |
ETagCollisionModal |
Modal for resolving concurrent edit conflicts |
SoftDeleteFilter |
Toggle to include/exclude soft-deleted records |
TenantSwitcher |
Tenant selector for multi-tenant APIs |
Field Widgets (@craft-easy/admin/components)¶
| Export | Description |
|---|---|
FieldRenderer |
Auto-renders the correct widget for a field schema |
TextInputField |
Single-line text input |
TextArea |
Multi-line text input |
NumberInput |
Numeric input with optional min/max/step |
Select |
Single-select dropdown |
MultiSelect |
Multi-select with checkboxes |
DatePicker |
Date-only picker |
DateTimePicker |
Date and time picker |
Toggle |
Boolean switch |
FileUpload |
File/binary upload |
RelationPicker |
Search-and-select for related resources |
TagInput |
Free-text tag list |
JsonEditor |
Raw JSON text editor |
Hooks (@craft-easy/admin/hooks)¶
| Export | Description |
|---|---|
useAdminSchema |
Fetches and caches the /admin/schema response |
useResource |
Fetches a single resource record by ID |
useResourceList |
Fetches a paginated resource list with filters |
useResourceMutations |
Returns create, update, remove mutation functions |
Stores (@craft-easy/admin/stores)¶
| Export | Description |
|---|---|
useApplicationStore |
Zustand store for managing API connections and active session |
applicationStore |
Raw store instance for use outside React |
Context (@craft-easy/admin/context)¶
| Export | Description |
|---|---|
ThemeProvider |
Wraps the app with light/dark theme support |
useTheme |
Returns current colors palette and mode |
Lib (@craft-easy/admin/lib)¶
| Export | Description |
|---|---|
createApiClient |
Factory for an authenticated API client with ETag support |
ApiError |
Error class thrown on non-2xx responses |
getListFields |
Extracts display fields from a resource schema |
getFormFields |
Extracts form fields from a resource schema |
getFieldLabel |
Returns the display label for a field |
formatCellValue |
Formats a raw field value for list display |
Types (@craft-easy/admin/types)¶
| Export | Description |
|---|---|
AdminSchema |
Root schema object from /admin/schema |
ResourceSchema |
Single resource definition |
FieldSchema |
Field metadata and widget type |
FormConfig / FormGroup |
Form group structure |
ListConfig |
List view configuration |
NavigationGroup |
Sidebar navigation group |
AuthConfig |
Auth endpoint configuration |
ThemeConfig |
API-driven theme overrides |
Application |
Stored API connection record |
Subpath Import Example¶
import { useAdminSchema, useResourceList } from '@craft-easy/admin/hooks';
import { FieldRenderer, ListView } from '@craft-easy/admin/components';
import { useApplicationStore } from '@craft-easy/admin/stores';
import { ThemeProvider, useTheme } from '@craft-easy/admin/context';
import type { AdminSchema, ResourceSchema } from '@craft-easy/admin/types';
import { createApiClient } from '@craft-easy/admin/lib';