0
# Content Manager
1
2
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.
3
4
## Context & Hook
5
6
React context and hook for accessing content manager state and operations.
7
8
```typescript { .api }
9
// Content manager context hook
10
interface CMEditViewDataManagerContextValue {
11
// Core data
12
initialData: ContentType;
13
modifiedData: ContentType;
14
formErrors: Record<string, TranslationMessage>;
15
16
// Layout and permissions
17
layout?: Schema.CollectionType | Schema.SingleType;
18
allLayoutData: {
19
components: Record<string, Schema.Component>;
20
contentType?: Schema.ContentType;
21
};
22
createActionAllowedFields: string[];
23
readActionAllowedFields: string[];
24
updateActionAllowedFields: string[];
25
26
// State flags
27
isCreatingEntry: boolean;
28
isSingleType: boolean;
29
hasDraftAndPublish: boolean;
30
shouldNotRunValidations?: boolean;
31
slug?: string;
32
status?: string;
33
34
// Publishing
35
publishConfirmation?: {
36
show: boolean;
37
draftCount: number;
38
};
39
onPublish?: () => Promise<unknown>;
40
onPublishPromptDismissal?: (e: React.SyntheticEvent) => Promise<void>;
41
onUnpublish?: () => Promise<unknown>;
42
43
// Form operations
44
onChange?: <TAttribute extends Attribute.Any>(
45
payload: {
46
target: { name: string; type: string; value: Attribute.GetValue<TAttribute> };
47
},
48
shouldSetInitialValue?: boolean
49
) => void;
50
51
// Component operations
52
addComponentToDynamicZone?: (
53
keys: string,
54
componentLayoutData: Record<string, unknown>,
55
allComponents: Record<string, unknown>,
56
shouldCheckErrors?: boolean,
57
position?: number
58
) => void;
59
addNonRepeatableComponentToField?: (
60
keys: string,
61
componentLayoutData: Schema.Component,
62
allComponents: Record<string, Schema.Component>
63
) => void;
64
addRepeatableComponentToField?: (
65
keys: string,
66
componentLayoutData: Record<string, unknown>,
67
allComponents: Record<string, unknown>,
68
shouldCheckErrors?: boolean,
69
position?: number
70
) => void;
71
removeComponentFromDynamicZone?: (dynamicZoneName: string, index: number) => void;
72
removeComponentFromField?: (key: string, uid: string) => void;
73
removeRepeatableField?: (key: string, uid?: string) => void;
74
75
// Component movement
76
moveComponentDown?: (dynamicZoneName: string, currentIndex: number) => void;
77
moveComponentUp?: (dynamicZoneName: string, currentIndex: number) => void;
78
moveComponentField?: (payload: { name: string; newIndex: number; currentIndex: number }) => void;
79
80
// Relations
81
relationConnect?: (payload: {
82
name: string;
83
value: { id: Entity['id'] };
84
toOneRelation?: boolean;
85
}) => void;
86
relationDisconnect?: (payload: { name: string; id: Entity['id'] }) => void;
87
relationLoad?: (payload: {
88
target: {
89
initialDataPath: string[];
90
modifiedDataPath: string[];
91
value: { id: Entity['id'] }[];
92
modifiedDataOnly?: boolean;
93
};
94
}) => void;
95
relationReorder?: (payload: { name: string; oldIndex: number; newIndex: number }) => void;
96
}
97
98
function useCMEditViewDataManager(): CMEditViewDataManagerContextValue;
99
100
// React context instance
101
const ContentManagerEditViewDataManagerContext: React.Context<CMEditViewDataManagerContextValue>;
102
```
103
104
**Usage Examples:**
105
106
```typescript
107
// Access content manager state and operations
108
const {
109
initialData,
110
modifiedData,
111
onChange,
112
onPublish,
113
isCreatingEntry,
114
hasDraftAndPublish,
115
formErrors
116
} = useCMEditViewDataManager();
117
118
// Handle form field changes
119
const handleFieldChange = (event) => {
120
onChange({
121
target: {
122
name: event.target.name,
123
type: event.target.type,
124
value: event.target.value
125
}
126
});
127
};
128
129
// Check if creating new entry
130
if (isCreatingEntry) {
131
// Show create-specific UI
132
}
133
134
// Handle publishing
135
const handlePublish = async () => {
136
if (onPublish) {
137
await onPublish();
138
}
139
};
140
```
141
142
## Content Type Interface
143
144
TypeScript interface for content type data structures.
145
146
```typescript { .api }
147
// Content type data interface
148
interface ContentType extends Partial<Entity> {
149
publishedAt?: string | null;
150
publishedBy?: User | null;
151
[key: string]: Attribute.GetValue<Attribute.Any> | null;
152
}
153
154
// Base entity interface
155
interface Entity {
156
id: StrapiEntity.ID;
157
createdAt: string | null;
158
createdBy: User | null;
159
updatedAt: string | null;
160
updatedBy: User | null;
161
}
162
163
// User interface for created/updated by fields
164
interface User {
165
id: StrapiEntity.ID;
166
createdAt: string;
167
createdBy: User | null;
168
updatedAt: string;
169
updatedBy: User | null;
170
firstname?: string;
171
lastname?: string;
172
username?: string;
173
email?: string;
174
isActive: boolean;
175
blocked: boolean;
176
roles: [];
177
}
178
```
179
180
**Usage Examples:**
181
182
```typescript
183
// Type-safe content handling
184
const handleContentUpdate = (content: ContentType) => {
185
// Access standard fields
186
const { id, createdAt, updatedAt, publishedAt } = content;
187
188
// Access custom fields (dynamically typed)
189
const title = content.title as string;
190
const description = content.description as string;
191
};
192
193
// Publishing workflow
194
const publishContent = (content: ContentType) => {
195
if (content.publishedAt) {
196
// Already published
197
} else {
198
// Draft content
199
}
200
};
201
```
202
203
## Utility Functions
204
205
Helper functions for content management operations and data transformation.
206
207
```typescript { .api }
208
// Remove specified fields from content data
209
function contentManagementUtilRemoveFieldsFromData<
210
TSchema extends Schema.ContentType,
211
TData extends { [K in keyof TSchema['attributes']]: Attribute.GetValue<TSchema['attributes'][K]> }
212
>(
213
data: TData,
214
contentTypeSchema: TSchema,
215
componentSchema: Record<string, Schema.Component>,
216
fields?: string[]
217
): TData;
218
219
// Format content type data for display/editing
220
function formatContentTypeData<
221
TSchema extends Schema.ContentType,
222
TData extends { [K in keyof TSchema['attributes']]: Attribute.GetValue<TSchema['attributes'][K]> }
223
>(
224
data: TData,
225
contentTypeSchema: TSchema,
226
componentSchema: Record<string, Schema.Component>
227
): TData;
228
229
// Get attribute information from schema
230
function getType(schema: Schema.Schema, attrName: string): string;
231
function getOtherInfos(schema: Schema.Schema, path: string[]): any;
232
```
233
234
**Usage Examples:**
235
236
```typescript
237
// Remove system fields before API submission
238
const cleanedData = contentManagementUtilRemoveFieldsFromData(
239
formData,
240
contentTypeSchema,
241
componentSchemas,
242
['createdBy', 'updatedBy', 'publishedAt'] // Optional custom fields to remove
243
);
244
245
// Format data for editing (adds temp keys for DnD)
246
const formattedData = formatContentTypeData(
247
rawData,
248
contentTypeSchema,
249
componentSchemas
250
);
251
252
// Get attribute type information
253
const fieldType = getType(schema, 'myField'); // Returns: 'string', 'number', 'relation', etc.
254
255
// Get nested attribute information
256
const isRepeatable = getOtherInfos(schema, ['myComponent', 'repeatable']);
257
const componentUid = getOtherInfos(schema, ['myComponent', 'component']);
258
```
259
260
## Integration Patterns
261
262
### Custom Field Components
263
264
```typescript
265
// Access content manager context in custom components
266
const MyCustomField = ({ name, ...props }) => {
267
const { modifiedData, onChange, formErrors } = useCMEditViewDataManager();
268
269
const fieldValue = modifiedData[name];
270
const fieldError = formErrors[name];
271
272
const handleChange = (value) => {
273
onChange({
274
target: { name, type: 'custom', value }
275
});
276
};
277
278
return (
279
<CustomInput
280
value={fieldValue}
281
onChange={handleChange}
282
error={fieldError}
283
{...props}
284
/>
285
);
286
};
287
```
288
289
### Component Management
290
291
```typescript
292
// Add components to dynamic zones
293
const handleAddComponent = () => {
294
const { addComponentToDynamicZone, allLayoutData } = useCMEditViewDataManager();
295
296
addComponentToDynamicZone(
297
'content', // Dynamic zone field name
298
componentLayout,
299
allLayoutData.components,
300
true, // Check for errors
301
0 // Position
302
);
303
};
304
305
// Handle repeatable components
306
const handleAddRepeatableComponent = () => {
307
const { addRepeatableComponentToField, allLayoutData } = useCMEditViewDataManager();
308
309
addRepeatableComponentToField(
310
'features', // Field name
311
componentLayout,
312
allLayoutData.components
313
);
314
};
315
```
316
317
### Relation Management
318
319
```typescript
320
// Connect relations
321
const handleRelationConnect = (selectedItem) => {
322
const { relationConnect } = useCMEditViewDataManager();
323
324
relationConnect({
325
name: 'category',
326
value: { id: selectedItem.id },
327
toOneRelation: true
328
});
329
};
330
331
// Reorder relation items
332
const handleRelationReorder = (oldIndex, newIndex) => {
333
const { relationReorder } = useCMEditViewDataManager();
334
335
relationReorder({
336
name: 'tags',
337
oldIndex,
338
newIndex
339
});
340
};
341
```
342
343
### Publishing Workflow
344
345
```typescript
346
// Handle publish/unpublish operations
347
const PublishButton = () => {
348
const {
349
hasDraftAndPublish,
350
onPublish,
351
onUnpublish,
352
modifiedData,
353
publishConfirmation
354
} = useCMEditViewDataManager();
355
356
if (!hasDraftAndPublish) return null;
357
358
const isPublished = !!modifiedData.publishedAt;
359
360
const handlePublishToggle = async () => {
361
if (isPublished && onUnpublish) {
362
await onUnpublish();
363
} else if (!isPublished && onPublish) {
364
await onPublish();
365
}
366
};
367
368
return (
369
<Button onClick={handlePublishToggle}>
370
{isPublished ? 'Unpublish' : 'Publish'}
371
</Button>
372
);
373
};
374
```
375
376
## Data Flow
377
378
The content manager context follows a unidirectional data flow:
379
380
1. **Initial Data**: Loaded from API into `initialData`
381
2. **Form Changes**: Updates flow to `modifiedData` via `onChange`
382
3. **Validation**: Errors stored in `formErrors`
383
4. **Submission**: Clean data sent to API via utility functions
384
5. **Publishing**: Draft/publish state managed via publish methods
385
386
This pattern ensures consistent state management and proper data handling throughout the content editing lifecycle.