Helper library for Strapi plugins development providing React components, hooks, utilities, and TypeScript types for building plugin interfaces
—
The @strapi/helper-plugin package provides comprehensive TypeScript interfaces and types for Strapi-specific data structures, API responses, component props, and system functionality. These types ensure type safety and developer experience across plugin development.
Types for internationalization and message handling throughout the Strapi admin interface.
// Translation message interface extending react-intl
interface TranslationMessage extends MessageDescriptor {
values?: Record<string, PrimitiveType>;
}
// Base message descriptor from react-intl
interface MessageDescriptor {
id: string;
defaultMessage?: string;
description?: string;
}
// Primitive types for translation values
type PrimitiveType = string | number | boolean | null | undefined | Date;Usage Examples:
// Define translatable messages
const messages: TranslationMessage = {
id: 'plugin.my-plugin.button.save',
defaultMessage: 'Save Changes',
values: {
count: 5,
name: 'Article'
}
};
// Use with react-intl
const { formatMessage } = useIntl();
const text = formatMessage(messages);Union types for various API error responses that can occur in Strapi applications.
// Union type covering all possible API errors
type ApiError =
| errors.ApplicationError
| errors.ForbiddenError
| errors.NotFoundError
| errors.NotImplementedError
| errors.PaginationError
| errors.PayloadTooLargeError
| errors.PolicyError
| errors.RateLimitError
| errors.UnauthorizedError
| errors.ValidationError
| errors.YupValidationError;
// Individual error type examples
interface ApplicationError {
name: 'ApplicationError';
message: string;
details?: Record<string, any>;
}
interface ValidationError {
name: 'ValidationError';
message: string;
details: {
errors: Array<{
path: string[];
message: string;
name: string;
}>;
};
}
interface ForbiddenError {
name: 'ForbiddenError';
message: string;
details?: Record<string, any>;
}Usage Examples:
// Handle different error types
const handleApiError = (error: ApiError) => {
switch (error.name) {
case 'ValidationError':
// Handle validation errors
error.details.errors.forEach(validationError => {
console.error(`Field ${validationError.path.join('.')}: ${validationError.message}`);
});
break;
case 'ForbiddenError':
// Handle permission errors
console.error('Access denied:', error.message);
break;
case 'ApplicationError':
// Handle general application errors
console.error('Application error:', error.message);
break;
default:
console.error('Unknown error:', error.message);
}
};Types for data filtering, querying, and search operations.
// Attribute-based filters
type AttributeFilter = Record<
string,
Record<EntityService.Params.Filters.Operator.Where, string | null>
>;
// Relation-based filters
type RelationFilter = Record<string, AttributeFilter>;
// Generic filter type
type Filter = AttributeFilter | RelationFilter;
// Filter operator interface
interface Operator {
value: EntityService.Params.Filters.Operator.Where;
intlLabel: MessageDescriptor;
}
// Comprehensive filter data configuration
interface FilterData {
name: string;
metadatas: {
label: string;
customOperators?: Array<{
intlLabel: { id: string; defaultMessage: string };
value: string;
}>;
customInput?: React.ComponentType;
options?: Array<{ label?: string; customValue: string }>;
uid?: string;
};
fieldSchema: {
type: Attribute.Any['type'];
options?: string[];
mainField?: {
name: string;
type?: Attribute.Any['type'];
};
};
trackedEvent?: TrackingEvent;
}
// Default filter input props
interface DefaultFilterInputsProps {
label?: string;
onChange: (value: string | null) => void;
options?: FilterData['fieldSchema']['options'] | FilterData['metadatas']['options'];
type: FilterData['fieldSchema']['type'];
value?: string | null;
}Usage Examples:
// Define filter configuration
const articleFilters: FilterData[] = [
{
name: 'title',
metadatas: {
label: 'Title',
customOperators: [
{
intlLabel: { id: 'filter.contains', defaultMessage: 'Contains' },
value: '$containsi'
}
]
},
fieldSchema: {
type: 'string'
}
},
{
name: 'category',
metadatas: {
label: 'Category',
uid: 'api::category.category'
},
fieldSchema: {
type: 'relation',
mainField: { name: 'name', type: 'string' }
}
}
];
// Create filter objects
const titleFilter: AttributeFilter = {
title: {
$containsi: 'react'
}
};
const categoryFilter: RelationFilter = {
category: {
name: {
$eq: 'Technology'
}
}
};Types specific to content management functionality (also covered in Content Manager documentation).
// Base entity interface
interface Entity {
id: StrapiEntity.ID;
createdAt: string | null;
createdBy: User | null;
updatedAt: string | null;
updatedBy: User | null;
}
// Content type with optional entity fields and dynamic attributes
interface ContentType extends Partial<Entity> {
publishedAt?: string | null;
publishedBy?: User | null;
[key: string]: Attribute.GetValue<Attribute.Any> | null;
}
// User interface for content relations
interface User extends NonNullableObject<Entity> {
firstname?: string;
lastname?: string;
username?: string;
email?: string;
isActive: boolean;
blocked: boolean;
roles: [];
}
// Helper type for non-nullable object properties
type NonNullableObject<T> = {
[key in keyof T]: NonNullable<T[key]>;
};Usage Examples:
// Work with content types
const article: ContentType = {
id: 1,
title: 'My Article',
content: 'Article content...',
publishedAt: '2023-01-01T00:00:00.000Z',
createdAt: '2023-01-01T00:00:00.000Z',
updatedAt: '2023-01-01T00:00:00.000Z'
};
// Type-safe property access
const title: string = article.title as string;
const isPublished: boolean = !!article.publishedAt;Common prop interfaces used across UI components.
// Permission-based component props
interface CheckPermissionsProps {
children: React.ReactNode;
permissions?: Array<{ action: string; subject: string }>;
}
// Form component props
interface FormProps extends Omit<FormikFormProps, 'noValidate'> {
children: React.ReactNode;
}
// Generic input props
interface GenericInputProps {
type: string;
name: string;
value?: any;
onChange?: (event: { target: { name: string; value: any; type: string } }) => void;
error?: string | TranslationMessage;
description?: string | TranslationMessage;
disabled?: boolean;
intlLabel?: TranslationMessage;
placeholder?: string | TranslationMessage;
required?: boolean;
step?: number;
max?: number;
min?: number;
attribute?: any;
}
// Table component props
interface TableProps<TRows extends { id: Entity.ID } = { id: Entity.ID }> {
children?: React.ReactNode;
contentType: string;
headers?: Array<TableHeader>;
rows?: Array<TRows>;
isLoading?: boolean;
withBulkActions?: boolean;
withMainAction?: boolean;
onConfirmDeleteAll?: (ids: Array<TRows['id']>) => Promise<void>;
onConfirmDelete?: (id: TRows['id']) => Promise<void>;
footer?: React.ReactNode;
action?: React.ReactNode;
}
// Table header definition
interface TableHeader {
name: string;
metadatas: {
sortable: boolean;
label: string;
mainField?: { name: string };
};
fieldSchema?: { type: string };
}Usage Examples:
// Type-safe component props
const MyFormComponent: React.FC<FormProps> = ({ children, ...props }) => {
return <Form {...props}>{children}</Form>;
};
// Table with typed rows
interface Article {
id: number;
title: string;
status: 'draft' | 'published';
}
const ArticleTable: React.FC = () => {
const tableProps: TableProps<Article> = {
contentType: 'articles',
headers: [
{ name: 'title', metadatas: { label: 'Title', sortable: true } },
{ name: 'status', metadatas: { label: 'Status', sortable: false } }
],
rows: articles,
withBulkActions: true,
onConfirmDelete: handleDelete
};
return <DynamicTable {...tableProps} />;
};Types for values returned by custom hooks.
// Fetch client hook return
interface FetchClientReturn {
get: <TData = any>(url: string, config?: any) => Promise<TData>;
post: <TData = any, TResponse = any>(url: string, data?: any, config?: any) => Promise<TResponse>;
put: <TData = any>(url: string, data?: any, config?: any) => Promise<TData>;
del: <TData = any>(url: string, config?: any) => Promise<TData>;
}
// RBAC hook return
interface AllowedActions {
[key: string]: boolean;
}
interface RBACReturn {
allowedActions: AllowedActions;
isLoading: boolean;
setIsLoading: () => void;
}
// Selection state hook return
interface SelectionActions<TValues> {
selectOne: (selection: TValues) => void;
selectAll: (nextSelections: TValues[]) => void;
selectOnly: (nextSelection: TValues) => void;
selectMultiple: (nextSelections: TValues[]) => void;
deselectMultiple: (nextSelections: TValues[]) => void;
setSelections: (selections: TValues[]) => void;
}
type UseSelectionStateReturn<TValues> = readonly [TValues[], SelectionActions<TValues>];
// Clipboard hook return
interface ClipboardReturn {
copy: (value: string | number) => Promise<boolean>;
}
// Query params hook return
interface QueryResult<TQuery> {
query: TQuery;
rawQuery: string;
}
type UseQueryParamsReturn<TQuery> = readonly [
QueryResult<TQuery>,
(nextParams: TQuery, method?: 'push' | 'remove') => void
];Usage Examples:
// Use typed hook returns
const { allowedActions, isLoading }: RBACReturn = useRBAC({
create: [{ action: 'create', subject: 'api::article.article' }]
});
// Selection state with typed values
interface SelectableItem {
id: number;
name: string;
}
const [selections, actions]: UseSelectionStateReturn<SelectableItem> = useSelectionState(
['id'],
[]
);
// Type-safe query parameters
interface QueryParams {
search?: string;
page?: number;
sort?: string;
}
const [{ query }, setQuery]: UseQueryParamsReturn<QueryParams> = useQueryParams({
page: 1,
sort: 'name:asc'
});Types for React context values used throughout the plugin system.
// App information context
interface AppInfoContextValue {
autoReload?: boolean;
communityEdition?: boolean;
currentEnvironment?: string;
dependencies?: Record<string, string>;
latestStrapiReleaseTag?: string;
nodeVersion?: string;
projectId?: string | null;
setUserDisplayName: (name: string) => void;
shouldUpdateStrapi: boolean;
strapiVersion?: string | null;
useYarn?: boolean;
userDisplayName: string;
userId?: string;
}
// Notification context
interface NotificationConfig {
blockTransition?: boolean;
link?: NotificationLink;
message?: string | TranslationMessage;
onClose?: () => void;
timeout?: number;
title?: string | TranslationMessage;
type?: 'info' | 'warning' | 'softWarning' | 'success';
}
interface NotificationsContextValue {
toggleNotification: (config: NotificationConfig) => void;
}
// RBAC context
interface Permission {
id?: Entity.ID;
action: string;
actionParameters?: object;
subject?: string | null;
properties?: {
fields?: string[];
locales?: string[];
[key: string]: any;
};
conditions?: string[];
}
interface RBACContextValue {
allPermissions: Permission[];
refetchPermissions: () => void;
}
// Tracking context
interface TrackingContextValue {
uuid?: string | boolean;
deviceId?: string;
telemetryProperties?: TelemetryProperties;
}
interface TelemetryProperties {
useTypescriptOnServer?: boolean;
useTypescriptOnAdmin?: boolean;
isHostedOnStrapiCloud?: boolean;
numberOfAllContentTypes?: number;
numberOfComponents?: number;
numberOfDynamicZones?: number;
}Usage Examples:
// Create typed context providers
const MyCustomProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const appInfo: AppInfoContextValue = useAppInfo();
const { toggleNotification }: NotificationsContextValue = useNotification();
const handleAction = () => {
if (appInfo.shouldUpdateStrapi) {
toggleNotification({
type: 'info',
message: 'Update available!'
});
}
};
return (
<div>
{children}
<button onClick={handleAction}>Check Updates</button>
</div>
);
};Types for parameters and return values of utility functions.
// HTTP client configuration
interface FetchClientOptions extends AxiosRequestConfig {
signal?: AbortSignal;
}
// Error normalization options
interface NormalizeErrorOptions {
name?: string;
intlMessagePrefixCallback?: (id: string) => string;
}
// Permission checking types
type PermissionToCheckAgainst = Pick<Permission, 'action' | 'subject'> &
Partial<Pick<Permission, 'actionParameters' | 'conditions' | 'properties'>>;
// Storage types
interface StorageItems {
userInfo: UserInfo;
jwtToken: string;
STRAPI_THEME: 'light' | 'dark';
GUIDED_TOUR_CURRENT_STEP: string | null;
GUIDED_TOUR_COMPLETED_STEPS: string[] | null;
GUIDED_TOUR_SKIPPED: boolean | null;
STRAPI_UPDATE_NOTIF: boolean | null;
STRAPI_UPLOAD_MODAL_VIEW: 0 | 1 | null;
STRAPI_UPLOAD_LIBRARY_VIEW: 0 | 1 | null;
videos: unknown;
onboarding: unknown;
}
type StorageItemValues = StorageItems[keyof StorageItems];Comprehensive type system for analytics and event tracking.
// Base event without properties
interface EventWithoutProperties {
name: 'didCreateEntry' | 'didDeleteEntry' | 'didEditEntry' | /* ... many more */;
properties?: never;
}
// Events with specific properties
interface CreateEntryEvents {
name: 'willCreateEntry' | 'didCreateEntry' | 'didNotCreateEntry';
properties: {
status?: string;
error?: unknown;
};
}
interface NavigationEvent {
name: 'willNavigate';
properties: {
from: string;
to: string;
};
}
// Union of all tracking events
type TrackingEvent = EventWithoutProperties | CreateEntryEvents | NavigationEvent | /* ... others */;
// Tracking hook interface
interface UseTrackingReturn {
trackUsage<TEvent extends TrackingEvent>(
event: TEvent['name'],
properties?: TEvent['properties']
): Promise<null | AxiosResponse<string>>;
}Usage Examples:
// Type-safe event tracking
const { trackUsage }: UseTrackingReturn = useTracking();
// Events without properties
trackUsage('didCreateEntry');
// Events with properties
trackUsage('willNavigate', {
from: '/admin/plugins',
to: '/admin/content-manager'
});
trackUsage('didCreateEntry', {
status: 'success'
});Helper types for generic operations and type transformations.
// Make all properties of an object non-nullable
type NonNullableObject<T> = {
[K in keyof T]: NonNullable<T[K]>;
};
// Empty object type
type EmptyObject = Record<string, never>;
// Callback ref type
type CallbackRef<T> = (instance: T | null) => void;
// Axios response wrapper
interface AxiosResponseWrapper<T> extends AxiosResponse<T> {
data: T;
}
// Generic ID types
type EntityID = string | number;
// Generic entity with ID
interface WithId {
id: EntityID;
}
// Partial with required ID
type PartialWithId<T> = Partial<T> & WithId;Usage Examples:
// Use generic types for type safety
interface MyEntity {
id: number;
name: string;
description?: string;
}
// Ensure all properties are non-null
type RequiredEntity = NonNullableObject<MyEntity>;
// Create partial updates with required ID
type EntityUpdate = PartialWithId<MyEntity>;
const updateEntity = (update: EntityUpdate) => {
// update.id is guaranteed to exist
// other properties are optional
console.log(`Updating entity ${update.id}`);
};// Extend base props with additional types
interface MyComponentProps extends GenericInputProps {
customOption?: boolean;
onCustomEvent?: () => void;
}
const MyComponent: React.FC<MyComponentProps> = (props) => {
return <GenericInput {...props} />;
};// Define API response shapes
interface ArticleListResponse {
data: ContentType[];
meta: {
pagination: {
page: number;
pageSize: number;
pageCount: number;
total: number;
};
};
}
// Use with fetch client
const { get }: FetchClientReturn = useFetchClient();
const response = await get<ArticleListResponse>('/api/articles');// Create typed error handlers
const handleTypedError = (error: unknown) => {
if (error && typeof error === 'object' && 'response' in error) {
const axiosError = error as AxiosError<{ error: ApiError }>;
const normalized = normalizeAPIError(axiosError);
if (normalized) {
console.error(normalized.defaultMessage);
}
}
};The comprehensive type system in @strapi/helper-plugin ensures type safety across all plugin development scenarios, from UI components to API interactions to data management operations.
Install with Tessl CLI
npx tessl i tessl/npm-strapi--helper-plugin