
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:
Check out all its features in the official Plate documentation.
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.
Let's set up Plate in your frontend application to integrate with Strapi:
npx create-react-app my-plate-app
cd my-plate-app
npm install @udecode/plate
npm install @udecode/plate-paragraph @udecode/plate-heading @udecode/plate-list
npm install axios
// 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;
Let's build a basic Plate editor component and connect it with Strapi:
// 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;
// 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;
// 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.
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.
Plate is a rich text editor framework built on Slate.js with a plugin architecture. It provides highly customizable editing experiences that can replace Strapi's default editor through custom field plugins.
Create a Strapi plugin that wraps Plate's React components, configure the desired plugins for formatting and features, then register it as a custom field type to replace the default WYSIWYG editor in your content types.
Yes, Plate includes plugins for real-time collaboration using WebSocket connections. Implement a collaboration server alongside Strapi to enable multiple editors working on the same content simultaneously.
Plate stores content as JSON using Slate's document model. Configure your Strapi content type to use a JSON field, then render the stored structure appropriately in your frontend application.
Yes, Plate's plugin architecture allows granular control over available features. Enable only the plugins you need—formatting, lists, tables, images—to create a focused editing experience tailored to your content requirements.