React meta-framework for building enterprise CRUD applications with authentication, data management, and headless UI integration
—
Resource management, loading states, metadata handling, notifications, and various utility functions for enhanced development experience.
Gets resource and parameter information from current context with automatic inference from routes.
/**
* Gets resource and parameter information from current context
* @param params - Resource parameter configuration
* @returns Current resource context and parameter values
*/
function useResourceParams(params?: UseResourceParamsConfig): UseResourceParamsReturnType;
interface UseResourceParamsConfig {
/** Override resource name */
resource?: string;
/** Override record ID */
id?: BaseKey;
/** Override action */
action?: string;
}
interface UseResourceParamsReturnType {
/** Current resource configuration */
resource?: IResourceItem;
/** Resource identifier (name) */
identifier?: string;
/** Current record ID */
id?: BaseKey;
/** Current action */
action?: Action;
/** Function to set ID */
setId: React.Dispatch<React.SetStateAction<BaseKey | undefined>>;
}
interface IResourceItem {
/** Resource name */
name: string;
/** Display label */
label?: string;
/** Resource icon */
icon?: React.ReactNode;
/** Whether resource can be deleted */
canDelete?: boolean;
/** Parent resource name */
parentName?: string;
/** Resource metadata */
meta?: Record<string, any>;
}
type Action = "list" | "create" | "edit" | "show" | "clone";Usage Example:
import { useResourceParams } from "@refinedev/core";
function ResourceHeader() {
const { resource, action, id } = useResourceParams();
const getTitle = () => {
if (!resource) return "Unknown Resource";
switch (action) {
case "list":
return `${resource.label || resource.name} List`;
case "create":
return `Create ${resource.label || resource.name}`;
case "edit":
return `Edit ${resource.label || resource.name} #${id}`;
case "show":
return `${resource.label || resource.name} #${id}`;
case "clone":
return `Clone ${resource.label || resource.name} #${id}`;
default:
return resource.label || resource.name;
}
};
return (
<header>
{resource?.icon}
<h1>{getTitle()}</h1>
</header>
);
}
// Dynamic component behavior based on resource
function ResourceActions() {
const { resource, action, id, setId } = useResourceParams();
const canEdit = resource?.meta?.permissions?.includes("edit");
const canDelete = resource?.canDelete !== false;
return (
<div className="resource-actions">
{action === "show" && canEdit && (
<button onClick={() => setId(id)}>
Edit {resource?.label}
</button>
)}
{action === "edit" && canDelete && (
<button className="danger">
Delete {resource?.label}
</button>
)}
</div>
);
}Tracks loading time for performance monitoring and user experience optimization.
/**
* Tracks loading time for performance monitoring
* @param params - Loading overtime configuration
* @returns Loading time information and callbacks
*/
function useLoadingOvertime(params: UseLoadingOvertimeConfig): UseLoadingOvertimeReturnType;
interface UseLoadingOvertimeConfig {
/** Whether currently loading */
isLoading: boolean;
/** Interval for time updates in milliseconds */
interval?: number;
/** Callback fired at each interval */
onInterval?: (elapsedTime: number) => void;
}
interface UseLoadingOvertimeReturnType {
/** Elapsed time since loading started in milliseconds */
elapsedTime?: number;
}Usage Example:
import { useLoadingOvertime, useList } from "@refinedev/core";
function DataTableWithLoadingIndicator() {
const { query } = useList({ resource: "posts" });
const { elapsedTime } = useLoadingOvertime({
isLoading: query.isLoading,
interval: 1000,
onInterval: (time) => {
if (time > 5000) {
console.warn("Query taking longer than expected:", time);
}
}
});
if (query.isLoading) {
return (
<div className="loading-state">
<div>Loading posts...</div>
{elapsedTime && elapsedTime > 2000 && (
<div className="loading-overtime">
Still loading... ({Math.round(elapsedTime / 1000)}s)
</div>
)}
</div>
);
}
return (
<div>
{/* Table content */}
</div>
);
}Manages metadata for resources and operations with context-aware defaults.
/**
* Manages metadata for resources and operations
* @param params - Meta configuration
* @returns Current metadata and update functions
*/
function useMeta(params?: UseMetaConfig): UseMetaReturnType;
interface UseMetaConfig {
/** Initial metadata */
meta?: MetaQuery;
/** Resource name for context */
resource?: string;
}
interface UseMetaReturnType {
/** Current metadata */
meta: MetaQuery;
/** Function to update metadata */
setMeta: (meta: MetaQuery) => void;
/** Function to merge metadata */
mergeMeta: (meta: MetaQuery) => void;
}
interface MetaQuery {
[key: string]: any;
}Access to the meta context provider for global metadata management.
/**
* Access to meta context provider
* @returns Meta context value and functions
*/
function useMetaContext(): MetaContextValue;
interface MetaContextValue {
/** Global metadata */
meta: MetaQuery;
/** Function to set global metadata */
setMeta: (meta: MetaQuery) => void;
}Shows notifications with different types, positions, and auto-dismiss functionality.
/**
* Shows notifications with customizable options
* @returns Notification functions for different types
*/
function useNotification(): UseNotificationReturnType;
interface UseNotificationReturnType {
/** Show success notification */
open: (params: OpenNotificationParams) => void;
/** Close specific notification */
close: (key: string) => void;
}
interface OpenNotificationParams {
/** Notification type */
type: "success" | "error" | "progress" | "warning" | "info";
/** Notification message */
message: string;
/** Additional description */
description?: string;
/** Unique key for the notification */
key?: string;
/** Whether notification should not auto-dismiss */
undoableTimeout?: number;
/** Whether to show undo button */
cancelMutation?: () => void;
/** Custom duration before auto-dismiss */
duration?: number;
}Usage Example:
import { useNotification, useCreate } from "@refinedev/core";
function CreatePostForm() {
const notification = useNotification();
const { mutate: createPost } = useCreate();
const handleCreate = (values: any) => {
createPost({
resource: "posts",
values
}, {
onSuccess: (data) => {
notification.open({
type: "success",
message: "Post Created",
description: `Post "${data.data.title}" has been created successfully.`,
duration: 4000
});
},
onError: (error) => {
notification.open({
type: "error",
message: "Creation Failed",
description: error.message || "Failed to create post."
});
}
});
};
return (
<form onSubmit={handleCreate}>
{/* Form fields */}
</form>
);
}
// Undoable notifications
function DeleteWithUndo({ postId }: { postId: string }) {
const notification = useNotification();
const { mutate: deletePost } = useDelete();
const handleDelete = () => {
let cancelMutation: (() => void) | undefined;
notification.open({
type: "success",
message: "Post Deleted",
description: "Post has been deleted.",
undoableTimeout: 5000,
cancelMutation: () => {
if (cancelMutation) {
cancelMutation();
notification.open({
type: "success",
message: "Deletion Cancelled",
description: "Post deletion has been cancelled."
});
}
}
});
// Set timeout for actual deletion
const timeoutId = setTimeout(() => {
deletePost({
resource: "posts",
id: postId
});
}, 5000);
cancelMutation = () => {
clearTimeout(timeoutId);
};
};
return <button onClick={handleDelete}>Delete Post</button>;
}Manages modal state with multiple modal support and proper cleanup.
/**
* Manages modal state with multiple modal support
* @returns Modal state and control functions
*/
function useModal(): UseModalReturnType;
interface UseModalReturnType {
/** Whether modal is visible */
visible: boolean;
/** Show the modal */
show: () => void;
/** Hide the modal */
close: () => void;
/** Toggle modal visibility */
toggle: () => void;
}Usage Example:
import { useModal, useOne } from "@refinedev/core";
function PostDetailsModal({ postId }: { postId?: string }) {
const modal = useModal();
const { data: post } = useOne({
resource: "posts",
id: postId!,
queryOptions: {
enabled: !!postId && modal.visible
}
});
return (
<>
<button onClick={modal.show}>View Details</button>
{modal.visible && (
<div className="modal-overlay" onClick={modal.close}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<div className="modal-header">
<h2>{post?.data.title}</h2>
<button onClick={modal.close}>×</button>
</div>
<div className="modal-body">
<p>{post?.data.content}</p>
</div>
</div>
</div>
)}
</>
);
}
// Multiple modals
function MultiModalExample() {
const editModal = useModal();
const deleteModal = useModal();
const viewModal = useModal();
return (
<div>
<button onClick={editModal.show}>Edit</button>
<button onClick={deleteModal.show}>Delete</button>
<button onClick={viewModal.show}>View</button>
{/* Multiple modal components */}
</div>
);
}Invalidates React Query cache with fine-grained control over cache keys.
/**
* Invalidates React Query cache with fine-grained control
* @returns Cache invalidation functions
*/
function useInvalidate(): UseInvalidateReturnType;
interface UseInvalidateReturnType {
/** Invalidate specific cache entries */
invalidate: (params: InvalidateParams) => Promise<void>;
/** Invalidate all cache entries for a resource */
invalidateAll: (resource: string) => Promise<void>;
}
interface InvalidateParams {
/** Resource name */
resource: string;
/** Specific record ID */
id?: BaseKey;
/** Data provider name */
dataProviderName?: string;
/** Whether to invalidate exact match only */
exact?: boolean;
}Usage Example:
import { useInvalidate, useUpdate } from "@refinedev/core";
function RefreshablePostList() {
const invalidate = useInvalidate();
const { mutate: updatePost } = useUpdate();
const handleRefresh = async () => {
// Invalidate all posts data
await invalidate.invalidateAll("posts");
};
const handleUpdatePost = (id: string, values: any) => {
updatePost({
resource: "posts",
id,
values
}, {
onSuccess: async () => {
// Invalidate specific post and list
await invalidate.invalidate({ resource: "posts", id });
await invalidate.invalidate({ resource: "posts" });
}
});
};
return (
<div>
<button onClick={handleRefresh}>Refresh All</button>
{/* Post list */}
</div>
);
}Utilities for handling file uploads, downloads, and format conversion.
/**
* Convert file to base64 string
* @param file - File to convert
* @returns Promise resolving to base64 string
*/
function file2Base64(file: File): Promise<string>;
/**
* CSV import data mapper
* @param data - Raw CSV data
* @param mapping - Field mapping configuration
* @returns Mapped data array
*/
function importCSVMapper(
data: any[],
mapping: Record<string, string>
): any[];
interface FileProcessingOptions {
/** Maximum file size in bytes */
maxSize?: number;
/** Allowed file types */
allowedTypes?: string[];
/** Custom validation function */
validate?: (file: File) => boolean | string;
}Usage Example:
import { file2Base64, importCSVMapper } from "@refinedev/core";
function FileUploadComponent() {
const handleFileUpload = async (file: File) => {
try {
// Convert to base64 for API upload
const base64 = await file2Base64(file);
// Upload to server
await fetch("/api/upload", {
method: "POST",
body: JSON.stringify({ file: base64 }),
headers: { "Content-Type": "application/json" }
});
} catch (error) {
console.error("Upload failed:", error);
}
};
const handleCSVImport = (csvData: any[]) => {
// Map CSV columns to database fields
const mappedData = importCSVMapper(csvData, {
"Full Name": "name",
"Email Address": "email",
"Phone Number": "phone"
});
// Process mapped data
console.log("Imported data:", mappedData);
};
return (
<div>
<input
type="file"
accept=".csv,.xlsx"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) handleFileUpload(file);
}}
/>
</div>
);
}Standardized query key generation for consistent React Query caching.
/**
* Query key utilities for React Query
*/
interface QueryKeys {
/** Generate keys for list queries */
list: (resource: string, params?: any) => string[];
/** Generate keys for single record queries */
one: (resource: string, id: BaseKey, params?: any) => string[];
/** Generate keys for many records queries */
many: (resource: string, ids: BaseKey[], params?: any) => string[];
/** Generate keys for custom queries */
custom: (resource: string, params?: any) => string[];
}
/**
* Query key builder for advanced cache management
*/
class KeyBuilder {
/** Set resource name */
resource(name: string): KeyBuilder;
/** Set action type */
action(action: string): KeyBuilder;
/** Set parameters */
params(params: any): KeyBuilder;
/** Build the key array */
get(): string[];
}Utilities for URL generation and route matching.
/**
* Generate user-friendly names from resource identifiers
* @param name - Resource name or identifier
* @returns User-friendly display name
*/
function useUserFriendlyName(): (name: string) => string;
/**
* Match resource from current route
* @param route - Current route path
* @param resources - Available resources
* @returns Matched resource configuration
*/
function matchResourceFromRoute(
route: string,
resources: IResourceItem[]
): IResourceItem | undefined;
/**
* Generate default document title
* @param params - Title generation parameters
* @returns Generated document title
*/
function generateDefaultDocumentTitle(params: {
resource?: string;
action?: string;
id?: BaseKey;
}): string;Utilities for object manipulation and property access.
/**
* Flatten nested object keys with dot notation
* @param obj - Object to flatten
* @param prefix - Key prefix for nested properties
* @returns Flattened object
*/
function flattenObjectKeys(
obj: Record<string, any>,
prefix?: string
): Record<string, any>;
/**
* Convert property path string to array
* @param path - Property path (e.g., "user.profile.name")
* @returns Path array (e.g., ["user", "profile", "name"])
*/
function propertyPathToArray(path: string): string[];Usage Example:
import {
flattenObjectKeys,
propertyPathToArray,
useUserFriendlyName
} from "@refinedev/core";
function ObjectUtilsExample() {
const getUserFriendlyName = useUserFriendlyName();
// Flatten nested object
const nestedData = {
user: {
profile: {
name: "John Doe",
address: {
city: "New York",
country: "USA"
}
}
}
};
const flatData = flattenObjectKeys(nestedData);
// Result: { "user.profile.name": "John Doe", "user.profile.address.city": "New York", ... }
// Convert path to array
const pathArray = propertyPathToArray("user.profile.name");
// Result: ["user", "profile", "name"]
// Generate friendly names
const friendlyName = getUserFriendlyName("user_profiles");
// Result: "User Profiles"
return (
<div>
<h2>{friendlyName}</h2>
<pre>{JSON.stringify(flatData, null, 2)}</pre>
</div>
);
}interface UtilityConfig {
/** Default configuration values */
defaults?: Record<string, any>;
/** Feature flags */
features?: Record<string, boolean>;
/** Custom settings */
settings?: Record<string, any>;
}
interface ProcessingResult<T> {
/** Processing success status */
success: boolean;
/** Processed data */
data?: T;
/** Processing errors */
errors?: string[];
/** Warning messages */
warnings?: string[];
}
interface CacheOptions {
/** Cache expiry time in milliseconds */
expiry?: number;
/** Whether to use stale data while revalidating */
staleWhileRevalidate?: boolean;
/** Custom cache key */
key?: string;
}
interface ErrorBoundaryProps {
/** Fallback component for errors */
fallback?: React.ComponentType<{ error: Error }>;
/** Error callback */
onError?: (error: Error, errorInfo: any) => void;
/** Children to wrap */
children: React.ReactNode;
}Publishes real-time events to subscribed clients for live data synchronization.
/**
* Publishes real-time events to subscribed clients
* @returns Publish function from the live provider
*/
function usePublish(): ((event: LiveEvent) => void) | undefined;
interface LiveEvent {
/** Channel name for the event */
channel: string;
/** Event type */
type: "created" | "updated" | "deleted" | string;
/** Event payload */
payload: {
/** IDs of affected records */
ids?: BaseKey[];
/** Additional event data */
[key: string]: any;
};
/** Timestamp of the event */
date: Date;
}Usage Example:
import { usePublish } from "@refinedev/core";
function ProductForm() {
const publish = usePublish();
const handleProductUpdate = (product) => {
// Update product logic...
// Notify other clients about the update
publish?.({
channel: "products",
type: "updated",
payload: {
ids: [product.id]
},
date: new Date()
});
};
return (
<form onSubmit={handleProductUpdate}>
{/* Form fields */}
</form>
);
}Subscribes to real-time events for live data synchronization and automatic updates.
/**
* Subscribes to real-time events for live data updates
* @param params - Subscription configuration
*/
function useSubscription(params: UseSubscriptionConfig): void;
interface UseSubscriptionConfig {
/** Channel name to subscribe to */
channel: string;
/** Callback function when live events are received */
onLiveEvent: (event: LiveEvent) => void;
/** Types of events to subscribe to */
types?: Array<"created" | "updated" | "deleted" | "*" | string>;
/** Whether subscription is enabled */
enabled?: boolean;
/** Additional parameters for the subscription */
params?: {
ids?: BaseKey[];
id?: BaseKey;
sorters?: CrudSort[];
filters?: CrudFilter[];
subscriptionType?: "useList" | "useOne" | "useMany";
resource?: string;
[key: string]: any;
};
/** Metadata for the subscription */
meta?: MetaQuery & { dataProviderName?: string };
}Usage Example:
import { useSubscription, useList } from "@refinedev/core";
function ProductsList() {
const { data, refetch } = useList({ resource: "products" });
// Subscribe to product updates
useSubscription({
channel: "products",
onLiveEvent: (event) => {
if (event.type === "created" || event.type === "updated" || event.type === "deleted") {
// Refetch the list when products are modified
refetch();
}
},
types: ["created", "updated", "deleted"],
enabled: true
});
return (
<div>
{data?.data.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}Manages live mode state and automatic real-time updates for data operations.
/**
* Manages live mode state for automatic real-time updates
* @returns Live mode configuration and state
*/
function useLiveMode(): UseLiveModeReturnType;
interface UseLiveModeReturnType {
/** Current live mode setting */
liveMode?: "auto" | "manual" | "off";
}Usage Example:
import { useLiveMode, useList } from "@refinedev/core";
function ProductsList() {
const { liveMode } = useLiveMode();
const { data } = useList({
resource: "products",
liveMode: liveMode, // Use global live mode setting
});
return (
<div>
<p>Live mode: {liveMode}</p>
{data?.data.map(product => (
<div key={product.id}>{product.name}</div>
))}
</div>
);
}Generates menu items for navigation based on available resources, with hierarchical structure support and customizable visibility.
/**
* Generates menu items for navigation sidebars and menus
* @param params - Menu configuration options
* @returns Menu items with navigation structure
*/
function useMenu(params?: UseMenuConfig): UseMenuReturnType;
interface UseMenuConfig {
/** Additional metadata for menu generation */
meta?: Record<string, any>;
/** Hide menu items that have missing route parameters */
hideOnMissingParameter?: boolean;
}
interface UseMenuReturnType {
/** Keys of menu items that should be open by default */
defaultOpenKeys: string[];
/** Currently selected menu item key */
selectedKey: string;
/** Hierarchical menu items structure */
menuItems: TreeMenuItem[];
}
interface TreeMenuItem {
/** Menu item key/identifier */
key: string;
/** Display label for the menu item */
label?: string;
/** Icon component for the menu item */
icon?: React.ReactNode;
/** Route path for navigation */
route?: string;
/** Child menu items */
children: TreeMenuItem[];
/** Resource metadata */
resource?: IResourceItem;
/** Parent resource reference */
parentName?: string;
}Usage Example:
import { useMenu } from "@refinedev/core";
function Sidebar() {
const { menuItems, selectedKey, defaultOpenKeys } = useMenu({
hideOnMissingParameter: true
});
const renderMenuItem = (item: TreeMenuItem) => (
<div key={item.key} className={selectedKey === item.key ? "active" : ""}>
{item.icon}
<span>{item.label}</span>
{item.children.length > 0 && (
<div>
{item.children.map(renderMenuItem)}
</div>
)}
</div>
);
return (
<nav>
{menuItems.map(renderMenuItem)}
</nav>
);
}Install with Tessl CLI
npx tessl i tessl/npm-refinedev--core