or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

content-manager.mdfeatures.mdhooks.mdindex.mdtypes.mdui-components.mdutilities.md

content-manager.mddocs/

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.