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:
// strapiService.js
import axios from 'axios';
const strapiUrl = 'http://localhost:1337';
const apiToken = 'YOUR_API_TOKEN';
const strapiService = {
async getArticles() {
const response = await axios.get(`${strapiUrl}/api/articles`, {
headers: {
Authorization: `Bearer ${apiToken}`
}
});
return response.data;
},
async createArticle(data) {
const response = await axios.post(`${strapiUrl}/api/articles`, { data }, {
headers: {
Authorization: `Bearer ${apiToken}`
}
});
return response.data;
}
};
export 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:
// MyPlateEditor.jsx
import React from 'react';
import { Plate, PlateProvider } from '@udecode/plate';
import { createPlugins } from '@udecode/plate-core';
import { createParagraphPlugin } from '@udecode/plate-paragraph';
import { createHeadingPlugin } from '@udecode/plate-heading';
const plugins = createPlugins([
createParagraphPlugin(),
createHeadingPlugin()
]);
const initialValue = [
{
type: 'p',
children: [{ text: 'Start typing here...' }]
}
];
const MyPlateEditor = ({ value = initialValue, onChange, readOnly = false }) => {
return (
<PlateProvider plugins={plugins} initialValue={value}>
<Plate
onChange={onChange}
editableProps={{ placeholder: 'Type something...', readOnly }}
/>
</PlateProvider>
);
};
export default MyPlateEditor;- Create a form component for new articles:
// CreateArticle.jsx
import React, { useState } from 'react';
import strapiService from './strapiService';
import MyPlateEditor from './MyPlateEditor';
const CreateArticle = () => {
const [title, setTitle] = useState('');
const [content, setContent] = useState([]);
const [isSubmitting, setIsSubmitting] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
setIsSubmitting(true);
try {
const serializedContent = JSON.stringify(content);
await strapiService.createArticle({
title,
content: serializedContent,
});
setTitle('');
setContent([]);
alert('Article created successfully!');
} catch (error) {
console.error('Error creating article:', error);
alert('Error creating article. Please try again.');
} finally {
setIsSubmitting(false);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="title">Title</label>
<input
id="title"
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Article Title"
required
/>
</div>
<div>
<label>Content</label>
<MyPlateEditor onChange={setContent} />
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Creating...' : 'Create Article'}
</button>
</form>
);
};
export default CreateArticle;- Create a component to display articles from Strapi:
// ArticleList.jsx
import React, { useEffect, useState } from 'react';
import strapiService from './strapiService';
import MyPlateEditor from './MyPlateEditor';
const ArticleList = () => {
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchArticles = async () => {
try {
const data = await strapiService.getArticles();
setArticles(data.data);
} catch (error) {
console.error('Error fetching articles:', error);
} finally {
setLoading(false);
}
};
fetchArticles();
}, []);
if (loading) {
return <div>Loading articles...</div>;
}
return (
<div>
<h1>Articles</h1>
{articles.length === 0 ? (
<p>No articles found</p>
) : (
articles.map((article) => (
<div key={article.id}>
<h2>{article.attributes.title}</h2>
<MyPlateEditor
value={JSON.parse(article.attributes.content)}
readOnly={true}
/>
</div>
))
)}
</div>
);
};
export 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.