Helper library for Strapi plugins development providing React components, hooks, utilities, and TypeScript types for building plugin interfaces
—
The @strapi/helper-plugin package provides content manager utilities and React context for managing content types, relations, and CRUD operations within the Strapi admin interface. These exports enable plugins to integrate seamlessly with Strapi's content management system.
React context and hook for accessing content manager state and operations.
// Content manager context hook
interface CMEditViewDataManagerContextValue {
// Core data
initialData: ContentType;
modifiedData: ContentType;
formErrors: Record<string, TranslationMessage>;
// Layout and permissions
layout?: Schema.CollectionType | Schema.SingleType;
allLayoutData: {
components: Record<string, Schema.Component>;
contentType?: Schema.ContentType;
};
createActionAllowedFields: string[];
readActionAllowedFields: string[];
updateActionAllowedFields: string[];
// State flags
isCreatingEntry: boolean;
isSingleType: boolean;
hasDraftAndPublish: boolean;
shouldNotRunValidations?: boolean;
slug?: string;
status?: string;
// Publishing
publishConfirmation?: {
show: boolean;
draftCount: number;
};
onPublish?: () => Promise<unknown>;
onPublishPromptDismissal?: (e: React.SyntheticEvent) => Promise<void>;
onUnpublish?: () => Promise<unknown>;
// Form operations
onChange?: <TAttribute extends Attribute.Any>(
payload: {
target: { name: string; type: string; value: Attribute.GetValue<TAttribute> };
},
shouldSetInitialValue?: boolean
) => void;
// Component operations
addComponentToDynamicZone?: (
keys: string,
componentLayoutData: Record<string, unknown>,
allComponents: Record<string, unknown>,
shouldCheckErrors?: boolean,
position?: number
) => void;
addNonRepeatableComponentToField?: (
keys: string,
componentLayoutData: Schema.Component,
allComponents: Record<string, Schema.Component>
) => void;
addRepeatableComponentToField?: (
keys: string,
componentLayoutData: Record<string, unknown>,
allComponents: Record<string, unknown>,
shouldCheckErrors?: boolean,
position?: number
) => void;
removeComponentFromDynamicZone?: (dynamicZoneName: string, index: number) => void;
removeComponentFromField?: (key: string, uid: string) => void;
removeRepeatableField?: (key: string, uid?: string) => void;
// Component movement
moveComponentDown?: (dynamicZoneName: string, currentIndex: number) => void;
moveComponentUp?: (dynamicZoneName: string, currentIndex: number) => void;
moveComponentField?: (payload: { name: string; newIndex: number; currentIndex: number }) => void;
// Relations
relationConnect?: (payload: {
name: string;
value: { id: Entity['id'] };
toOneRelation?: boolean;
}) => void;
relationDisconnect?: (payload: { name: string; id: Entity['id'] }) => void;
relationLoad?: (payload: {
target: {
initialDataPath: string[];
modifiedDataPath: string[];
value: { id: Entity['id'] }[];
modifiedDataOnly?: boolean;
};
}) => void;
relationReorder?: (payload: { name: string; oldIndex: number; newIndex: number }) => void;
}
function useCMEditViewDataManager(): CMEditViewDataManagerContextValue;
// React context instance
const ContentManagerEditViewDataManagerContext: React.Context<CMEditViewDataManagerContextValue>;Usage Examples:
// Access content manager state and operations
const {
initialData,
modifiedData,
onChange,
onPublish,
isCreatingEntry,
hasDraftAndPublish,
formErrors
} = useCMEditViewDataManager();
// Handle form field changes
const handleFieldChange = (event) => {
onChange({
target: {
name: event.target.name,
type: event.target.type,
value: event.target.value
}
});
};
// Check if creating new entry
if (isCreatingEntry) {
// Show create-specific UI
}
// Handle publishing
const handlePublish = async () => {
if (onPublish) {
await onPublish();
}
};TypeScript interface for content type data structures.
// Content type data interface
interface ContentType extends Partial<Entity> {
publishedAt?: string | null;
publishedBy?: User | null;
[key: string]: Attribute.GetValue<Attribute.Any> | null;
}
// Base entity interface
interface Entity {
id: StrapiEntity.ID;
createdAt: string | null;
createdBy: User | null;
updatedAt: string | null;
updatedBy: User | null;
}
// User interface for created/updated by fields
interface User {
id: StrapiEntity.ID;
createdAt: string;
createdBy: User | null;
updatedAt: string;
updatedBy: User | null;
firstname?: string;
lastname?: string;
username?: string;
email?: string;
isActive: boolean;
blocked: boolean;
roles: [];
}Usage Examples:
// Type-safe content handling
const handleContentUpdate = (content: ContentType) => {
// Access standard fields
const { id, createdAt, updatedAt, publishedAt } = content;
// Access custom fields (dynamically typed)
const title = content.title as string;
const description = content.description as string;
};
// Publishing workflow
const publishContent = (content: ContentType) => {
if (content.publishedAt) {
// Already published
} else {
// Draft content
}
};Helper functions for content management operations and data transformation.
// Remove specified fields from content data
function contentManagementUtilRemoveFieldsFromData<
TSchema extends Schema.ContentType,
TData extends { [K in keyof TSchema['attributes']]: Attribute.GetValue<TSchema['attributes'][K]> }
>(
data: TData,
contentTypeSchema: TSchema,
componentSchema: Record<string, Schema.Component>,
fields?: string[]
): TData;
// Format content type data for display/editing
function formatContentTypeData<
TSchema extends Schema.ContentType,
TData extends { [K in keyof TSchema['attributes']]: Attribute.GetValue<TSchema['attributes'][K]> }
>(
data: TData,
contentTypeSchema: TSchema,
componentSchema: Record<string, Schema.Component>
): TData;
// Get attribute information from schema
function getType(schema: Schema.Schema, attrName: string): string;
function getOtherInfos(schema: Schema.Schema, path: string[]): any;Usage Examples:
// Remove system fields before API submission
const cleanedData = contentManagementUtilRemoveFieldsFromData(
formData,
contentTypeSchema,
componentSchemas,
['createdBy', 'updatedBy', 'publishedAt'] // Optional custom fields to remove
);
// Format data for editing (adds temp keys for DnD)
const formattedData = formatContentTypeData(
rawData,
contentTypeSchema,
componentSchemas
);
// Get attribute type information
const fieldType = getType(schema, 'myField'); // Returns: 'string', 'number', 'relation', etc.
// Get nested attribute information
const isRepeatable = getOtherInfos(schema, ['myComponent', 'repeatable']);
const componentUid = getOtherInfos(schema, ['myComponent', 'component']);// Access content manager context in custom components
const MyCustomField = ({ name, ...props }) => {
const { modifiedData, onChange, formErrors } = useCMEditViewDataManager();
const fieldValue = modifiedData[name];
const fieldError = formErrors[name];
const handleChange = (value) => {
onChange({
target: { name, type: 'custom', value }
});
};
return (
<CustomInput
value={fieldValue}
onChange={handleChange}
error={fieldError}
{...props}
/>
);
};// Add components to dynamic zones
const handleAddComponent = () => {
const { addComponentToDynamicZone, allLayoutData } = useCMEditViewDataManager();
addComponentToDynamicZone(
'content', // Dynamic zone field name
componentLayout,
allLayoutData.components,
true, // Check for errors
0 // Position
);
};
// Handle repeatable components
const handleAddRepeatableComponent = () => {
const { addRepeatableComponentToField, allLayoutData } = useCMEditViewDataManager();
addRepeatableComponentToField(
'features', // Field name
componentLayout,
allLayoutData.components
);
};// Connect relations
const handleRelationConnect = (selectedItem) => {
const { relationConnect } = useCMEditViewDataManager();
relationConnect({
name: 'category',
value: { id: selectedItem.id },
toOneRelation: true
});
};
// Reorder relation items
const handleRelationReorder = (oldIndex, newIndex) => {
const { relationReorder } = useCMEditViewDataManager();
relationReorder({
name: 'tags',
oldIndex,
newIndex
});
};// Handle publish/unpublish operations
const PublishButton = () => {
const {
hasDraftAndPublish,
onPublish,
onUnpublish,
modifiedData,
publishConfirmation
} = useCMEditViewDataManager();
if (!hasDraftAndPublish) return null;
const isPublished = !!modifiedData.publishedAt;
const handlePublishToggle = async () => {
if (isPublished && onUnpublish) {
await onUnpublish();
} else if (!isPublished && onPublish) {
await onPublish();
}
};
return (
<Button onClick={handlePublishToggle}>
{isPublished ? 'Unpublish' : 'Publish'}
</Button>
);
};The content manager context follows a unidirectional data flow:
initialDatamodifiedData via onChangeformErrorsThis pattern ensures consistent state management and proper data handling throughout the content editing lifecycle.
Install with Tessl CLI
npx tessl i tessl/npm-strapi--helper-plugin