These integration guides are not official documentation and the Strapi Support Team will not provide assistance with them.
Why Use Plate?
Plate is a rich text editing framework built on Slate.js that offers a highly tailored editing experience. Its plugin-based architecture lets you add, remove, and customize features to match exactly what your project needs.
What makes Plate special is how it combines flexibility with ready-to-use functionality. By integrating Plate with Strapi, you get built-in components for common editing features while keeping complete control—perfect for headless CMS platforms. This integration enhances visual editing in Strapi, offering real-time visualization and editing from the frontend to CMS fields, making content creation more intuitive and efficient.
Key benefits of Plate include:
- Support for collaborative editing when multiple users work on the same document is available with Strapi. You can create a real-time collaboration document editing tool using Strapi, WebSockets, and Vue.js as demonstrated in a tutorial on their blog.
- Customizable UI and theming to match your application's design
- Strong TypeScript support for better development, including tools for automatic type generation and autocompletion, is available. You can start a new TypeScript project in Strapi or add TypeScript support to an existing project (see the benefits of TypeScript).
- AI integration capabilities for smarter editing features
- Advanced rich-text editing features are part of Strapi's new rich-text editor, which offers a true WYSIWYG field. This allows content editors to directly edit and format content, supporting drag-and-drop paragraph rearrangement and Notion-like shortcuts for efficient content management.
Check out all its features in the official Plate documentation.
Getting Started with Plate
Integrating Plate with Strapi creates a powerful content management system with advanced editing capabilities. This section walks through the complete process from environment setup to implementation.
Installing Plate and Adjusting Settings
Let's set up Plate in your frontend application to integrate with Strapi:
- Create a React project (if needed):
npx create-react-app my-plate-app
cd my-plate-app
- Install Plate dependencies:
npm install @udecode/plate
- Install optional Plate plugins with the following command:
npm install @udecode/plate-paragraph @udecode/plate-heading @udecode/plate-list
- Set up Axios for Strapi API communication:
npm install axios
- Create a service file for interacting with the Strapi API:
1// strapiService.js
2import axios from 'axios';
3
4const strapiUrl = 'http://localhost:1337';
5const apiToken = 'YOUR_API_TOKEN';
6
7const strapiService = {
8 async getArticles() {
9 const response = await axios.get(`${strapiUrl}/api/articles`, {
10 headers: {
11 Authorization: `Bearer ${apiToken}`
12 }
13 });
14 return response.data;
15 },
16
17 async createArticle(data) {
18 const response = await axios.post(`${strapiUrl}/api/articles`, { data }, {
19 headers: {
20 Authorization: `Bearer ${apiToken}`
21 }
22 });
23 return response.data;
24 }
25};
26
27export default strapiService;
Code Implementation for Plate and Strapi Integration
Let's build a basic Plate editor component and connect it with Strapi:
- Create the Plate editor component:
1// MyPlateEditor.jsx
2import React from 'react';
3import { Plate, PlateProvider } from '@udecode/plate';
4import { createPlugins } from '@udecode/plate-core';
5import { createParagraphPlugin } from '@udecode/plate-paragraph';
6import { createHeadingPlugin } from '@udecode/plate-heading';
7
8const plugins = createPlugins([
9 createParagraphPlugin(),
10 createHeadingPlugin()
11]);
12
13const initialValue = [
14 {
15 type: 'p',
16 children: [{ text: 'Start typing here...' }]
17 }
18];
19
20const MyPlateEditor = ({ value = initialValue, onChange, readOnly = false }) => {
21 return (
22 <PlateProvider plugins={plugins} initialValue={value}>
23 <Plate
24 onChange={onChange}
25 editableProps={{ placeholder: 'Type something...', readOnly }}
26 />
27 </PlateProvider>
28 );
29};
30
31export default MyPlateEditor;
- Create a form component for new articles:
1// CreateArticle.jsx
2import React, { useState } from 'react';
3import strapiService from './strapiService';
4import MyPlateEditor from './MyPlateEditor';
5
6const CreateArticle = () => {
7 const [title, setTitle] = useState('');
8 const [content, setContent] = useState([]);
9 const [isSubmitting, setIsSubmitting] = useState(false);
10
11 const handleSubmit = async (e) => {
12 e.preventDefault();
13 setIsSubmitting(true);
14
15 try {
16 const serializedContent = JSON.stringify(content);
17 await strapiService.createArticle({
18 title,
19 content: serializedContent,
20 });
21
22 setTitle('');
23 setContent([]);
24 alert('Article created successfully!');
25 } catch (error) {
26 console.error('Error creating article:', error);
27 alert('Error creating article. Please try again.');
28 } finally {
29 setIsSubmitting(false);
30 }
31 };
32
33 return (
34 <form onSubmit={handleSubmit}>
35 <div>
36 <label htmlFor="title">Title</label>
37 <input
38 id="title"
39 type="text"
40 value={title}
41 onChange={(e) => setTitle(e.target.value)}
42 placeholder="Article Title"
43 required
44 />
45 </div>
46
47 <div>
48 <label>Content</label>
49 <MyPlateEditor onChange={setContent} />
50 </div>
51
52 <button type="submit" disabled={isSubmitting}>
53 {isSubmitting ? 'Creating...' : 'Create Article'}
54 </button>
55 </form>
56 );
57};
58
59export default CreateArticle;
- Create a component to display articles from Strapi:
1// ArticleList.jsx
2import React, { useEffect, useState } from 'react';
3import strapiService from './strapiService';
4import MyPlateEditor from './MyPlateEditor';
5
6const ArticleList = () => {
7 const [articles, setArticles] = useState([]);
8 const [loading, setLoading] = useState(true);
9
10 useEffect(() => {
11 const fetchArticles = async () => {
12 try {
13 const data = await strapiService.getArticles();
14 setArticles(data.data);
15 } catch (error) {
16 console.error('Error fetching articles:', error);
17 } finally {
18 setLoading(false);
19 }
20 };
21
22 fetchArticles();
23 }, []);
24
25 if (loading) {
26 return <div>Loading articles...</div>;
27 }
28
29 return (
30 <div>
31 <h1>Articles</h1>
32 {articles.length === 0 ? (
33 <p>No articles found</p>
34 ) : (
35 articles.map((article) => (
36 <div key={article.id}>
37 <h2>{article.attributes.title}</h2>
38 <MyPlateEditor
39 value={JSON.parse(article.attributes.content)}
40 readOnly={true}
41 />
42 </div>
43 ))
44 )}
45 </div>
46 );
47};
48
49export default ArticleList;
By integrating Plate with Strapi, you've created a working content management system with advanced editing capabilities. You can create rich text content with the Plate editor, store it in your Strapi backend, and display that content in your application.
For more advanced setups, consider extending this integration with additional Plate plugins, customizing the editor toolbar, adding collaborative editing, or creating more sophisticated content workflows.
Strapi Open Office Hours
If you have any questions about Strapi 5 or just would like to stop by and say hi, you can join us at Strapi's Discord Open Office Hours Monday through Friday at 12:30 pm - 1:30 pm CST: Strapi Discord Open Office Hours
For more details, visit the Strapi documentation and Plate documentation.