October 2024 Summaries
30 posts from Strapi
Filter
Month:
Year:
Post Summaries
Back to Blog
Over 75% of global consumers prefer products in their native language, highlighting the importance of multilingual websites for reaching a broader audience. To effectively manage and optimize multilingual content, various tools are available such as Weglot, Transifex, Lokalise, Smartling, and Phrase. These tools streamline translation processes and enhance user experience on global websites. Additionally, implementing best practices like using hreflang tags, clear URL structures, adapting content to cultural nuances, translating metadata, and integrating user-friendly language switchers can improve multilingual SEO performance. For e-commerce stores, integrating multilingual features is crucial for a smooth shopping experience. Selecting the right tools for your multilingual website involves considering factors like translation systems, SEO tools, scalability, audience preferences, and support for specific languages. By understanding your audience's needs and choosing appropriate tools, you can achieve international success with platforms like Strapi Internationalization (i18n) Plugin.
Oct 29, 2024
694 words in the original blog post.
WordPress has been facing numerous vulnerabilities due to plugins, affecting millions of users. This article discusses the reasons why WordPress is becoming obsolete and suggests considering a switch from WordPress to Strapi. The main issues with WordPress include performance problems due to its use of PHP and traditional databases, security risks as it's widely targeted by hackers, plugin compatibility issues, limitations in modern web development capabilities, and limited customization options. Strapi is presented as a strong alternative to WordPress, offering features that meet modern development needs and providing numerous benefits for businesses.
Oct 29, 2024
765 words in the original blog post.
This tutorial teaches how to use the Strapi headless CMS for image uploads and management using its REST API with Postman and Next.js. It covers creating a Food Collection Type in Strapi, enabling permissions for image upload via REST API, installing Next.js, creating global types for the application, and creating starter components such as SubmitButton, MultipleOrSingleUpload, LinkToSpecificEntry, LinkedImages, and UploadAtEntryCreation. The tutorial also explains how to configure the Strapi Upload plugin and provides an overview of different media uploads supported by Strapi.
Oct 28, 2024
2,222 words in the original blog post.
In 2025, AI tools are set to revolutionize software development by enhancing productivity and efficiency. These include code assistants like GitHub Copilot, machine learning platforms such as TensorFlow, and AI-enhanced IDEs that provide predictive coding and intelligent debugging. By integrating these tools into their workflow, developers can address the challenges of software development, leading to faster project completion. Selecting an appropriate AI tool depends on a developer's specific needs and compatibility with existing tools. It is crucial to maintain a balance where AI improves productivity without diminishing human creativity.
Oct 28, 2024
956 words in the original blog post.
AI tools can significantly improve eCommerce conversion rates by up to 30% and customer satisfaction by up to 20%. Some of the best AI tools for eCommerce include Jasper.ai, Clerk.io, ChatGPT, Salesforce Einstein, ViSenze, Nosto, OptiMonk, Amazon Personalize, Narrato AI, Frase, and MarketMuse. These tools can enhance content creation, product recommendations, customer support, personalization, SEO, and overall eCommerce performance. Integrating these tools with a headless CMS like Strapi can offer even more flexibility and scalability for your online store.
Oct 27, 2024
686 words in the original blog post.
In a 2024 article by Statista, it was reported that 84% of software developers regularly use source code collaboration tools such as GitHub, GitLab, and Bitbucket. These tools promote teamwork and help execute large projects timely and with minimal errors. The article explores various best collaboration tools for developers, categorized by their type.
Content management systems (CMS) play a critical role in managing and delivering content. A headless CMS overview explains how this modern architecture separates the frontend from the backend, allowing developers to use any preferred frontend framework like React, Vue.js, or Angular. This separation enhances flexibility and enables the use of the same content across different platforms without being tied to a specific front-end technology.
Version control systems help developers manage changes to source code over time, which allows teams to work on code simultaneously, track revisions, and revert to previous versions if needed. GitHub and GitLab are popular version control tools that offer additional features like code hosting, issue tracking, pull requests, and built-in continuous integration and deployment (CI/CD) pipelines.
Project management tools help teams organize tasks, track progress, and manage workflows. Jira, Trello, and Asana are some popular project management tools that offer features such as issue tracking, sprint planning, customizable workflows, task assignments, deadline tracking, and project visualization.
Effective communication is essential for development teams, especially when working remotely. Slack and Microsoft Teams are widely used messaging platforms that allow real-time communication, file sharing, and integration with various development tools.
Design collaboration tools enable developers and designers to work together on UI/UX designs. Figma and Miro are popular design collaboration tools that offer features like real-time collaboration, online whiteboard for visual collaboration, brainstorming, planning, and diagramming.
Real-time code collaboration tools enhance productivity by allowing developers to work together simultaneously, providing instant feedback and supporting collaborative coding practices. Visual Studio Code Live Share is an example of a tool that enables developers to co-edit and debug code in real-time.
Tools like Jira and Trello help teams organize tasks, monitor progress, and streamline workflows by enabling sprint planning, real-time updates, and team collaboration. Instant messaging and video conferencing tools keep development teams connected, especially when working remotely. Slack and Zoom are popular communication platforms that offer features like direct messaging, group chats, file sharing, and reliable video conferencing.
Integrating multiple tools can effectively cover different aspects of collaboration. Developers can also explore headless CMS best practices to enhance content management workflows. Choosing the right collaboration tools for developers boosts your team's productivity and drives project success by integrating platforms like GitHub for code collaboration, Slack for communication, and Figma for design.
Real-world examples illustrate how integrating the right tools can lead to successful project outcomes. Exploring other Strapi use cases can provide insights into how other teams have effectively improved their workflows. With Strapi, you can find the perfect plan for your business needs, offering performance and flexibility in its latest version, Strapi v5.
Oct 25, 2024
1,040 words in the original blog post.
Strapi is an open-source, API-first content management system (CMS) that offers a flexible and scalable solution for enterprises. Its headless architecture allows businesses to create custom content management solutions tailored to their needs. Strapi supports both RESTful and GraphQL APIs, enabling seamless integration with existing systems and services. The CMS is designed for scalability, supporting horizontal scaling and caching mechanisms for consistent performance. Additionally, Strapi offers robust security features such as single sign-on (SSO), role-based access control (RBAC), data encryption, and regular security updates to meet compliance requirements in various industries. Overall, Strapi's customization options, API capabilities, and flexible architecture make it a strong choice for enterprises seeking a powerful and adaptable CMS solution.
Oct 25, 2024
970 words in the original blog post.
In this tutorial, we continued building our YouTube clone by adding user authentication and profile management functionality. We used the Flutter framework to create a mobile application that interacts with a Strapi CMS backend. Here's a summary of what we learned in this tutorial:
1. Implemented user authentication using Provider package and JWT tokens.
2. Created an AuthScreen widget for signing up or logging in users.
3. Stored user data in the UserProvider class and updated UI based on user login status.
4. Added a ProfileScreen widget to display user profile information, manage videos, and upload new videos.
5. Used ImagePicker package to pick thumbnail images and video files from the device gallery.
6. Implemented video playback preview using VideoPlayerController.
7. Updated HomeScreen navigation to access ProfileScreen after signing in or signing up.
The complete code for this tutorial is available here on my Github repository. I hope you enjoyed this series. Happy coding!
Oct 24, 2024
4,249 words in the original blog post.
In this tutorial, we will learn how to set up a new Flutter project, configure permissions, create app services, and state management to handle real-time functionalities and UI updates. We will also build the frontend with Flutter and consume APIs to implement a functional YouTube clone application. The code for this project is available on Github.
Oct 21, 2024
3,468 words in the original blog post.
Authentication in Strapi verifies user identities to secure access to content and APIs, while authorization determines what actions authenticated users can perform within the application. To implement authentication, developers can use local authentication with email and password or integrate third-party providers like Google, Facebook, or GitHub. Authorization involves defining roles and permissions that specify user capabilities. Strapi supports Role-Based Access Control (RBAC) to manage user access by assigning roles with specific permissions. JWT tokens are used for stateless authentication in Strapi, which should be stored securely and included in subsequent API requests. To enhance security, developers can protect routes using policies and middleware, implement HTTPS, enable rate limiting, regularly update roles and permissions, restrict admin panel access, enable two-factor authentication, and use logging and monitoring features like the Strapi Audit Log.
Oct 20, 2024
1,348 words in the original blog post.
This guide provides a comprehensive walkthrough of migrating from Prismic to Strapi, a popular headless content management system (CMS). The process involves several steps, including evaluating your current content structure in Prismic, installing and setting up Strapi, recreating content types, exporting data from Prismic, importing and transforming data into Strapi, updating frontend integration to use Strapi API, setting up and configuring Strapi, migrating media assets, testing and verifying migration, handling Prismic-specific features in Strapi, and considering SEO implications during the migration process. By following this guide, users can effectively transition from Prismic to Strapi while maximizing their content management capabilities.
Oct 19, 2024
1,544 words in the original blog post.
In this tutorial series, we will learn how to build a YouTube clone using Strapi as the backend CMS and Flutter for the frontend. Part one focuses on setting up the Strapi backend, creating collections, defining data relationships, creating custom endpoints for liking, commenting, and viewing videos, setting up Socket.io for real-time updates, and implementing lifecycle methods to listen to collection events. The main branches of the code are available on Github, with separate branches for each part of the tutorial series. In the next part, we will build the frontend using Flutter and connect it to the backend APIs to create a functional YouTube clone application.
Oct 16, 2024
3,550 words in the original blog post.
This tutorial guides users through creating a Strapi plugin that fetches YouTube video transcripts. It covers setting up a new Strapi CMS project, initializing the plugin, configuring it, and implementing necessary components such as routes, services, and controllers. The final result is a functional Strapi plugin capable of fetching YouTube video transcripts using the youtubei.js library.
Oct 15, 2024
1,640 words in the original blog post.
Scaling eCommerce operations efficiently is crucial for growing online businesses. Using Strapi's flexible CMS, you can scale ecommerce, expand your online store, handle increased traffic, and enhance performance without compromising user experience. Essential steps to help you expand your online store with Strapi include optimizing website content, tailoring your eCommerce platform, expanding sales channels, establishing a solid foundation, understanding budget and forecasting expenses, streamlining localization, leveraging headless CMS technology, automating processes, implementing project management tools, utilizing data analytics and AI-powered chatbots. By strategically planning and using tools like Strapi, you can manage content efficiently, customize features to your needs, and integrate seamlessly with other platforms.
Oct 15, 2024
793 words in the original blog post.
This guide explains how to use Strapi's headless CMS to integrate APIs, set up a development environment, choose the right technology stack, and configure development tools to streamline your workflow. By integrating with various third-party tools and services, Strapi can communicate and interact with external systems. Key points include using RESTful and GraphQL APIs for integrations, setting up webhooks for real-time communication, creating custom API routes, and writing custom code within the Strapi application. Integrating Strapi with popular third-party tools like Cloudinary, Auth0, and Firebase Authentication can enhance functionality and improve user experience. Careful evaluation of integration value, compatibility, documentation, security measures, testing, and maintenance is crucial for successful implementation.
Oct 13, 2024
995 words in the original blog post.
Strapi is a headless CMS that provides a secure framework for eCommerce applications to integrate with trusted payment gateways like Stripe and PayPal. It supports API security measures, including HTTPS encryption, data encryption at rest, validating inputs, and regularly updating the platform. Strapi also complies with PCI DSS standards by integrating with PCI-compliant services, reducing the application's compliance scope. To enhance security further, developers can define user roles and permissions using RBAC, encrypt sensitive data during API transactions, and maintain audit logs to monitor CMS actions. By following these best practices, Strapi enables eCommerce businesses to protect customer payment information while maintaining flexibility in their applications.
Oct 12, 2024
794 words in the original blog post.
Strapi is an open-source headless CMS that offers high flexibility, efficiency, and scalability for content management systems. Its decoupled architecture allows integration with various frontend frameworks like React, Vue, or Angular, enabling the use of preferred tools without limitations. Strapi's modular architecture supports plugin enhancements and customization to tailor it to specific project needs. It also offers a user-friendly interface for content structuring and management, version control, and role-based permissions for efficient collaboration.
Strapi's API-first approach accelerates development by generating APIs based on content models and supports rapid deployments, reducing time-to-deploy from weeks to hours. It enables centralized content management and delivery across platforms, improving user experience through flexible distribution and adaptability to changing requirements. Strapi also enhances applications' ability to handle high traffic and large data volumes by connecting to cloud services like Strapi Cloud and implementing caching strategies.
Strapi supports dynamic content delivery across various channels and seamlessly integrates with a wide range of frontend frameworks, allowing developers to choose the most suitable one for their projects. As an open-source CMS, it offers significant cost benefits by providing access to its source code without licensing fees or hidden costs. Additionally, pre-built integrations and plugins from the Strapi Marketplace help reduce development time and costs further.
Integrating Strapi into your tech stack can significantly improve content management efficiency, flexibility, and scalability, allowing you to build powerful, user-friendly applications with enhanced performance and cost savings.
Oct 11, 2024
808 words in the original blog post.
Strapi offers a range of SEO plugins that can enhance your website's visibility and attract more organic traffic. The platform's flexible design, including robust content modeling capabilities, makes it easy to incorporate SEO best practices. Key features include managing meta titles and descriptions with SERP previews, optimizing social media posts with previews, and SEO analysis with helpful insights. Strapi supports both REST and GraphQL APIs, allowing you to choose the best method for fetching and delivering content. By integrating these features, you can simplify your optimization process and improve your website's visibility in search engines. Leveraging Strapi's flexibility with frontend frameworks like Next.js can further enhance your site's SEO capabilities. To maximize your site's SEO potential, install the official Strapi SEO plugin or explore third-party options for advanced features. Regularly update your content and SEO settings to align with current best practices and optimize Strapi performance to enhance your site's speed and user experience.
Oct 11, 2024
1,061 words in the original blog post.
In this tutorial, we'll build a Blog Post Preview feature using Strapi 5 Draft and Publish feature along with Astro. We will create an API endpoint in our Astro project that allows us to control the visibility of posts directly from the frontend. This setup allows you to manage draft and published blog posts efficiently. Thanks to Astro, the website remains fast, and SEO-friendly and Strapi's Draft and Publish feature ensures a smooth content review process.
To follow this tutorial, you need:
1. Node.js installed on your machine (version 14 or higher).
2. An understanding of JavaScript and React basics.
3. A basic knowledge of Astro and Strapi.
Let's get started!
First, let's create a new project using the following command:
```bash
npx astro create strapi-draft-publish-feature
```
This will create a new directory named `strapi-draft-publish-feature`. Navigate into this directory and install Strapi CLI globally:
```bash
cd strapi-draft-publish-feature
npm i -g @strapi/cli
```
Now, let's create a new Strapi project inside our Astro project. Run the following command in your terminal:
```bash
npx create-strapi-app api --quickstart
```
This will prompt you to choose some options for setting up your Strapi project. Choose any name and description you like, then select "Node.js" as the package manager and "SQLite" as the database.
Once the installation is complete, start both Astro and Strapi servers:
```bash
npm run dev
```
Now that we have our development environments set up, let's create a new Content-Type in Strapi called "Post". Go to Content-Types Builder > Create New Content Type > Add another Content-Type. Name it "Post" and click on the "Continue" button.
In the next screen, add the following fields:
1. Title (Text)
2. Content (Rich Text)
3. Header Image (Media)
4. Publish Date (Date & Time)
5. Draft (Boolean)
Save and publish these changes. Now you should see a new Post entity in your Strapi admin panel under the "Content" section.
Next, let's create an API route for handling our actions on posts. In the terminal, navigate to the `strapi-draft-publish-feature` directory and run:
```bash
npm install @strapi/[email protected] strapi-plugin-documentation strapi-plugin-upload strapi-plugin-content-manager [email protected] strapi-utils
```
This will install the necessary plugins and utilities needed for our API route. Now, create a new file at `src/api/post/controllers/post.js` with this content:
```javascript
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async publish(ctx) {
try {
const { id } = ctx.params;
const result = await strapi.service('api::post.post').publishPostDocument(id);
ctx.send(result);
} catch (err) {
ctx.throw(500, err);
}
},
async unpublish(ctx) {
try {
const { id } = ctx.params;
const result = await strapi.service('api::post.post').unpublishPostDocument(id);
ctx.send(result);
} catch (err) {
ctx.throw(500, err);
}
},
async discard(ctx) {
try {
const { id } = ctx.params;
const result = await strapi.service('api::post.post').discardDraftDocument(id);
ctx.send(result);
} catch (err) {
ctx.throw(500, err);
}
},
};
```
And lastly, create a new file at `src/api/post/routes/post-actions.js` to define the new API routes for these actions:
```javascript
// src/api/post/routes/post-actions.js
module.exports = {
routes: [
{
method: "POST",
path: "/posts/:id/publish",
handler: "post.publish",
config: {
auth: {
strategies: ["api-token"],
},
},
},
{
method: "POST",
path: "/posts/:id/unpublish",
handler: "post.unpublish",
config: {
auth: {
strategies: ["api-token"],
},
},
},
{
method: "DELETE",
path: "/posts/:id/discard",
handler: "post.discard",
config: {
auth: {
strategies: ["api-token"],
},
},
},
],
};
```
publish()
, discard()
and discardDraft
As you can see, we have defined the strategy as api-token. This means those endpoints can only be called with a valid Bearer Token. So, let's create this token in the UI.
Go to Settings > API Token and click Create new API Token.
Name the token whatever you like and choose the token duration you want. The important part is the permissions. If you've followed all the steps, you should see the three actions we've added in the Permissions section. Only select these three since it's best practice to provision tokens with as little access as needed. With that, create the token and copy it.
Head back to your Astro codebase and open the `.env` file. Paste in the token as STRAPI_TOKEN. While you're at it, also define a SECRET variable and set whatever value you like. We will use this as the password to execute these actions in the frontend.
Your Astro environment file `.env` should now look like this:
```bash
STRAPI_URL=http://localhost:1337
STRAPI_TOKEN=your-token
SECRET=your-secret
```
We cannot call our Strapi API directly from the frontend, since it requires a secret token. We can only store that token in code that runs on the server. Otherwise, we risk exposing it to all users. Therefore, we need a very simple server endpoint. Luckily, Astro supports that out of the box.
Create a new file at `src/pages/api/content.js` with this content:
```javascript
export const prerender = false;
const STRAPI_URL = import.meta.env.STRAPI_URL;
const STRAPI_TOKEN = import.meta.env.STRAPI_TOKEN;
const SECRET = import.meta.env.SECRET;
export const POST = async ({ request }) => {
if (request.headers.get("Content-Type") === "application/json") {
const body = await request.json();
const { action, postId, secret } = body;
if (!action || !postId || !secret) {
return new Response(
JSON.stringify({ error: "Missing action, postId, or secret" }),
{ status: 400 }
);
}
if (secret !== SECRET) {
return new Response(JSON.stringify({ error: "Invalid secret" }), {
status: 401,
});
}
let endpoint = "";
switch (action) {
case "publish":
endpoint = `${STRAPI_URL}/api/posts/${postId}/publish`;
break;
case "unpublish":
endpoint = `${STRAPI_URL}/api/posts/${postId}/unpublish`;
break;
case "discard":
endpoint = `${STRAPI_URL}/api/posts/${postId}/discard`;
break;
default:
return new Response(JSON.stringify({ error: "Invalid action" }), {
status: 400,
});
}
try {
const strapiResponse = await fetch(endpoint, {
method: action === "discard" ? "DELETE" : "POST",
headers: {
Authorization: `Bearer ${STRAPI_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({}),
});
if (!strapiResponse.ok) {
throw new Error(`Strapi API error: ${strapiResponse.statusText}`);
}
const data = await strapiResponse.json();
return new Response(JSON.stringify(data), { status: 200 });
} catch (error) {
console.error("Error calling Strapi API:", error);
return new Response(JSON.stringify({ error: "Internal server error" }), {
status: 500,
});
}
}
return new Response(JSON.stringify({ error: "Invalid content type" }), {
status: 400,
});
};
```
In this section, we check that the client has sent the correct password. If that’s the case, we forward the request to Strapi along with the secret API Token. You can think of it as a type of proxy.
Up to this point, the whole application has not had any client-side code. Everything was rendered on the server and then sent to the client as static HTML. But to send actions to our API, we need some JavaScript on the client.
Vanilla JavaScript would be all you need for this. But we want to use this opportunity to show Astro's React integration. If you feel adventurous, you can use Vanilla JavaScript or another library like Vue or Svelte.
So, let's install React. All that's needed is one Astro command:
```bash
npx astro add react
```
Let's create the first component at `src/components/Visibility.jsx` with:
```javascript
import React from 'react';
export default function Visibility({ postId, isDraft, slug }) {
const handleVisibilityChange = async () => {
const action = isDraft ? 'publish' : 'unpublish';
const secret = prompt(`Please enter the secret to ${action} this post:`);
if (!secret) {
console.log(`${action.charAt(0).toUpperCase() + action.slice(1)} operation cancelled`);
return;
}
try {
const response = await fetch('/api/content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: action,
postId: postId,
secret: secret,
}),
});
if (!response.ok) {
throw new Error(`Failed to ${action} post`);
}
const data = await response.json();
console.log(`Post ${action}ed successfully`, data);
// Redirect to the appropriate URL based on the new state
const newPath = isDraft ? `/blog/${slug}` : `/blog/draft/${slug}`;
window.location.href = newPath;
} catch (error) {
console.error(`Error ${action}ing post:`, error);
alert(`Failed to ${action} post. Please try again.`);
}
};
return (
<button
onClick={handleVisibilityChange}
className={`px-4 py-2 ${
isDraft
? 'bg-emerald-500 hover:bg-emerald-600 focus:ring-emerald-500'
: 'bg-amber-500 hover:bg-amber-600 focus:ring-amber-500'
} text-per-neutral-900 font-semibold rounded-md focus:outline-none focus:ring-2 focus:ring-opacity-50 transition-all duration-200 border border-per-neutral-200 shadow-sm hover:shadow-md`}
>
{isDraft ? 'Publish' : 'Unpublish'}
</button>
);
}
```
This component is a button that toggles the visibility of a blog post between published and draft states, requiring a secret for authentication and updating the server via an API call. It takes the ID of the post and whether it's published or a draft as props.
A Published entry can be unpublished, and vice versa.
Now create the second component at `src/components/Discard.jsx` with:
```javascript
import React from 'react';
export default function Discard({ postId }) {
const handleDiscard = async () => {
// Prompt the user for the secret
const secret = prompt("Please enter the secret to discard this post:");
// If the user cancels the prompt or enters an empty string, abort the operation
if (!secret) {
console.log('Discard operation cancelled');
return;
}
try {
const response = await fetch('/api/content', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'discard',
postId: postId,
secret: secret,
}),
});
if (!response.ok) {
throw new Error('Failed to discard post');
}
const data = await response.json();
// Handle successful discard
console.log('Post discarded successfully', data);
// You might want to trigger some UI update here
} catch (error) {
console.error('Error discarding post:', error);
// Handle error (e.g., show an error message to the user)
alert('Failed to discard post. Please try again.');
}
};
return (
<button
onClick={handleDiscard}
className="px-4 py-2 bg-rose-500 text-per-neutral-900 font-semibold rounded-md hover:bg-rose-600 focus:outline-none focus:ring-2 focus:ring-rose-500 focus:ring-opacity-50 transition-all duration-200 border border-per-neutral-200 shadow-sm hover:shadow-md"
>
Discard
</button>
);
};
```
Since draft entries can be discarded, this component renders a "Discard" button that, when clicked, prompts the user for a secret and sends a request to discard a post, handling the response and potential errors.
In `src/pages/blog/[slug].astro`, add the Visibility button into your markup:
```javascript
<BlogLayout title={post.title}>
<article class="prose prose-lg max-w-2xl mx-auto py-24">
{
headerImage && (
<img
src={`${import.meta.env.STRAPI_URL}${headerImage}`}
alt={post.title}
class="mb-6 w-full h-auto rounded-lg"
/>
)
}
<div class="flex items-center justify-between mb-4">
<h1 class="mb-0">{post.title}</h1>
</div>
<div class="mb-6">
<Visibility client:load postId={post.documentId} isDraft={false} slug={slug} />
</div>
<p class="text-per-neutral-900 mt-2">{publishDate}</p>
<div set:html={marked.parse(post.content)} />
</article>
</BlogLayout>
```
Observe the `client:load` directive. This tells Astro to ship the JavaScript of this component to the client. This is essential for performing the API call. If you remove it, the button will still be rendered - without any of the functionality.
In `src/pages/blog/draft/[slug].astro`, do the same thing, but this time with the Discard component as well:
```javascript
<BlogLayout title={post.title}>
<article class="prose prose-lg max-w-2xl mx-auto py-24">
{
headerImage && (
<img
src={`${import.meta.env.STRAPI_URL}${headerImage}`}
alt={post.title}
class="mb-6 w-full h-auto rounded-lg"
/>
)
}
<div class="flex items-center justify-between mb-4">
<h1 class="mb-0">{post.title}</h1>
<p
class="text-base font-semibold bg-yellow-100 text-yellow-800 px-3 py-1 rounded flex-shrink-0"
>
Draft
</p>
</div>
<div class="mb-6">
<Discard client:load postId={post.documentId} />
</div>
<p class="text-per-neutral-900 mt-2">{publishDate}</p>
<div set:html={marked.parse(post.content)} />
</article>
</BlogLayout>
```
Now, let's test our implementation by creating a new post in Strapi and trying out the visibility buttons on the frontend. You should see that when you click on "Publish" or "Unpublish", the corresponding action is performed on the backend, and the page reloads to reflect the updated state of the post.
That's it! We have successfully built a Blog Post Preview feature using Strapi 5 Draft and Publish feature along with Astro. You can now use this setup in your own projects to manage draft and published blog posts efficiently.
Oct 10, 2024
4,750 words in the original blog post.
In this tutorial, we will build a dynamic website using Next.js 14 and Strapi 5. We will create a team member detail page that displays different types of content blocks such as spoilers, testimonials, and rich text. To achieve this, we will use the Blocks API provided by Strapi to manage our content blocks.
First, let's install the required dependencies:
```bash
npm install strapi @strapi/create-strapi-app @strapi/utils @strapi/types @strapi/react-sdk @strapi/react-model @strapi/react-form @strapi/react-query @strapi/react-ui-test @strapi/react-bootstrap
```
Next, let's create a new Strapi project:
```bash
npx create-strapi-app strapi-nextjs --quickstart
```
Now, let's install the Next.js 14 app and connect it to our Strapi backend:
```bash
npx create-next-app@latest --ts
cd nextjs-app
npm run dev
```
Inside the `pages/team/[slug].tsx` file, let's add the following code:
```typescript
import { useRouter } from "next/router";
import getTeamMember from "../../../utils/getTeamMember";
interface TeamPageBlock {
__component: string;
id: number;
}
const BlockRenderer = ({ block }: { block: TeamPageBlock }) => {
switch (block.__component) {
case "blocks.spoiler":
return <div>Spoiler Block</div>;
case "blocks.testimonial":
return <div>Testimonial Block</div>;
case "blocks.rich-text":
return <div>Rich Text Block</div>;
default:
return null;
}
};
const TeamMemberDetail = ({ params }: { params: { slug: string } }) => {
const router = useRouter();
if (!router.isReady) return <p>Loading...</p>;
const { slug } = router.query;
if (!slug) return <p>No member found</p>;
const teamMember = (await getTeamMember(slug)) as UserProfile;
return (
<div>
{teamMember.blocks.map((block: TeamPageBlock) => (
<BlockRenderer key={block.id} block={block} />
))}
</div>
);
};
export default TeamMemberDetail;
```
For the above code to work, we must create our new BlockRenderer component and all the other components we use on our Team Member Detail page.
Let's do that now.
Let's create a new folder in our app/components folder and name it blocks. Let's create a new file and name it index.tsx.
Now, let's create the following components in our blocks folder:
- spoiler-block.tsx
- testimonial-block.tsx
- rich-text-block.tsx
Here is what the spoiler-block.tsx file should look like:
```typescript
import { useState } from "react";
export interface SpoilerBlock {
__component: "blocks.spoiler";
id: number;
title: string;
content: string;
}
export function SpoilerBlock({ block }: { block: SpoilerBlock }) {
const [isExpanded, setIsExpanded] = useState(false);
return (
<div className="w-full mb-4 rounded-lg overflow-hidden shadow-md transition-all duration-300 ease-in-out">
<button
className={`w-full flex justify-between items-center p-4 bg-gray-100 hover:bg-gray-200 transition-colors duration-300 ease-in-out ${
isExpanded ? "bg-gray-200" : ""
}`}
onClick={() => setIsExpanded(!isExpanded)}
aria-expanded={isExpanded}
>
<span className="text-lg font-semibold text-gray-800">
{block.title}
</span>
<span
className={`text-2xl text-gray-600 transition-transform duration-300 ease-in-out ${
isExpanded ? "transform rotate-180" : ""
}`}
>
{isExpanded ? "-" : "+"}
</span>
</button>
<div
className={`overflow-hidden transition-all duration-300 ease-in-out ${
isExpanded ? "max-h-[1000px]" : "max-h-0"
}`}
aria-hidden={!isExpanded}
>
<div className="p-4 bg-white text-gray-700 leading-relaxed">
{block.content}
</div>
</div>
</div>
);
}
```
Here is what the testimonial-block.tsx file should look like:
```typescript
import Image from "next/image";
export interface TestimonialBlock {
__component: "blocks.testimonial";
id: number;
authorName: string;
quote: string;
photo: {
id: number;
documentId: string;
alternativeText: string | null;
name: string;
url: string;
};
}
export function TestimonialBlock({ block }: { block: TestimonialBlock }) {
const imageUrl = `${process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:1337"}${block?.photo?.url}`;
return (
<figure className="relative bg-gray-100 rounded-lg border border-gray-200 overflow-hidden my-6">
<div className="grid grid-cols-1 md:grid-cols-3">
<div className="relative h-64 md:h-full col-span-1">
<Image
src={imageUrl}
alt={block.photo.alternativeText || block.authorName}
layout="fill"
objectFit="cover"
className="w-full h-full object-center"
/>
</div>
<div className="p-8 col-span-2 flex flex-col justify-center">
<blockquote className="relative">
<svg
className="absolute top-0 left-0 transform -translate-x-6 -translate-y-8 h-16 w-16 text-gray-300 opacity-50"
fill="currentColor"
viewBox="0 0 32 32"
aria-hidden="true"
>
<path d="M9.352 4C4.456 7.456 1 13.12 1 19.36c0 5.088 3.072 8.064 6.624 8.064 3.36 0 5.856-2.688 5.856-5.856 0-3.168-2.208-5.472-5.088-5.472-.576 0-1.344.096-1.536.192.48-3.264 3.552-7.104 6.624-9.024L9.352 4zm16.512 0c-4.8 3.456-8.256 9.12-8.256 15.36 0 5.088 3.072 8.064 6.624 8.064 3.264 0 5.856-2.688 5.856-5.856 0-3.168-2.304-5.472-5.184-5.472-.576 0-1.248.096-1.44.192.48-3.264 3.456-7.104 6.528-9.024L25.864 4z" />
</svg>
<p className="relative text-xl font-medium text-gray-900 mb-4">
{block.quote}
</p>
</blockquote>
<figcaption className="font-semibold text-indigo-600 mt-2">
{block.authorName}
</figcaption>
</div>
</div>
</figure>
);
}
```
Here is what the rich-text-block.tsx file should look like:
```typescript
import { BlocksRenderer, type BlocksContent } from "@strapi/blocks-react-renderer";
import Image from "next/image";
export interface RichTextBlock {
__component: "blocks.rich-text";
id: number;
content: BlocksContent;
}
// This renderer is using Strapi's Rich Text renderer.
// https://github.com/strapi/blocks-react-renderer
export function RichTextBlock({ block }: { block: RichTextBlock }) {
return (
<div className="richtext">
<BlocksRenderer
content={block.content}
blocks={{
image: ({ image }) => {
console.log("image", image);
if (!image) return null;
return (
<div className="my-4 flex justify-center">
<Image
src={image.url}
width={image.width || 800}
height={image.height || 600}
alt={image.alternativeText || ""}
className="rounded-lg shadow-md h-[300px] w-full object-cover"
/>
</div>
);
},
}}
/>
</div>
);
}
```
This component is using Strapi's Rich Text renderer. You can learn more about it here;
To style our Rich Text component, we use Tailwind CSS. Here is what I added in the global.css file:
```css
/* Rich Text Block Start */
.richtext h1,
.richtext h2,
.richtext h3,
.richtext h4,
.richtext h5,
.richtext h6 {
@apply font-bold leading-tight;
}
.richtext h1 {
@apply text-4xl mb-6 text-gray-900 dark:text-gray-100;
}
.richtext h2 {
@apply text-3xl mb-4 text-gray-800 dark:text-gray-200;
}
.richtext h3 {
@apply text-2xl mb-3 text-gray-700 dark:text-gray-300;
}
.richtext h4 {
@apply text-xl mb-2 text-gray-600 dark:text-gray-400;
}
.richtext h5 {
@apply text-lg mb-2 text-gray-600 dark:text-gray-400;
}
.richtext h6 {
@apply text-base mb-2 text-gray-600 dark:text-gray-400;
}
.richtext p {
@apply mb-4 text-gray-700 dark:text-gray-300 leading-relaxed;
}
.richtext blockquote {
@apply border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-900 dark:bg-opacity-20 italic my-8 p-4 rounded-r-lg;
}
.richtext a {
@apply text-blue-600 dark:text-blue-400 hover:underline;
}
.richtext ul,
.richtext ol {
@apply mb-4 pl-8;
}
.richtext ul {
@apply list-disc;
}
.richtext ol {
@apply list-decimal;
}
/* Rich Text Block End */
```
Nice, now that we have all of our components, let's add the following code in our components/blocks/index.tsx file:
```typescript
import { RichTextBlock } from "./rich-text-block";
import { TestimonialBlock } from "./testimonial-block";
import { SpoilerBlock } from "./spoiler-block";
type TeamPageBlock = SpoilerBlock | TestimonialBlock | RichTextBlock;
const blocks: Record<TeamPageBlock["__component"], React.ComponentType<{ block: TeamPageBlock }>> = {
"blocks.spoiler": ({ block }: { block: TeamPageBlock }) => (
<SpoilerBlock block={block as SpoilerBlock} />
),
"blocks.testimonial": ({ block }: { block: TeamPageBlock }) => (
<TestimonialBlock block={block as TestimonialBlock} />
),
"blocks.rich-text": ({ block }: { block: TeamPageBlock }) => (
<RichTextBlock block={block as RichTextBlock} />
),
};
function BlockRenderer({ block }: { block: TeamPageBlock }) {
const BlockComponent = blocks[block.__component];
return BlockComponent ? <BlockComponent block={block} /> : null;
}
export { BlockRenderer };
export type { TeamPageBlock };
```
The TeamPageBlock type is a union of three block types: SpoilerBlock, TestimonialBlock, and RichTextBlock. Each block represents a distinct type of content that can be displayed.
Each block component is imported and can be rendered based on the block data type.
The blocks object maps a block's __component field (which is a string identifier like blocks.spoiler, blocks.testimonial, or blocks.rich-text) to the corresponding React component that should handle that block type.
For example:
When the block's __component is blocks.spoiler, it renders the SpoilerBlock component.
Each block type is cast to the correct type using TypeScript's as keyword (block as SpoilerBlock) to ensure the correct component is used with the correct block data.
The BlockRenderer component takes a TeamPageBlock object as a prop and renders the appropriate component based on the block's __component field.
Inside this function, blocks[block.__component] retrieves the correct component for the given block. If a matching component is found, it is rendered; otherwise, null is returned (indicating no content is rendered for unrecognized block types).
How the BlockRenderer Works
When BlockRenderer is called with a block, it looks up the block.__component field in the blocks object to find the corresponding React component.
It then renders the component, passing the block data to it.
The specific component (e.g., SpoilerBlock, TestimonialBlock, or RichTextBlock) knows how to handle and render that particular block's data.
Example Flow:
A TeamPageBlock (e.g., a TestimonialBlock) is passed to BlockRenderer.
BlockRenderer checks the block's __component field (e.g., blocks.testimonial).
It finds the corresponding component (TestimonialBlock) in the blocks object.
It renders the TestimonialBlock component, passing the block as a prop and displaying the block content.
This pattern is useful for rendering dynamic content in which blocks are of different types and each type has its own specific rendering logic.
Nice. Now that we have all our components, let's add some data for our team members in Strapi's admin panel. Then, check out our locally running Next.js app to see our new team member page.
Oct 08, 2024
6,919 words in the original blog post.
A headless CMS focuses on structured content management, providing tools to organize and manage content in a structured way, making it easier to deliver consistent content across multiple channels. On the other hand, BaaS provides basic unstructured data storage and backend services that allow you to store data without imposing a specific structure. Choosing between Headless CMS and backend frameworks depends on your project's needs, with a headless CMS offering flexibility, control, and robust content management, while BaaS provides speed, simplicity, and pre-built components.
Oct 07, 2024
1,860 words in the original blog post.
The text provides an in-depth guide on migrating from WordPress to Strapi, a headless CMS that offers more flexibility and control over content management. It outlines the key differences between these platforms, reasons for migration, steps to prepare for migration, setting up Strapi, data migration process, handling media files, customizing the backend, frontend integration, testing and optimization, and finally going live with Strapi. The guide emphasizes on the benefits of using a headless CMS like Strapi, including improved performance, developer-friendly environment, API-first approach, and scalability.
Oct 07, 2024
1,806 words in the original blog post.
When selecting a hosting solution for your headless CMS, consider factors such as scalability, performance, security, and cost. Cloud-based hosting options like Strapi Cloud, Amazon Web Services (AWS), and Google Cloud Platform offer scalability and reduced maintenance but may require more technical expertise. On-premises hosting allows full control over the environment and data but requires more resources to manage servers, security, and updates. Top providers for headless CMS hosting include Strapi Cloud, Cloudways, AWS, Hostinger, and Vercel. To migrate your headless CMS to a hosting provider, evaluate your organization's needs, plan the migration process, and focus on ongoing management. Best practices for managing your headless CMS hosting include regular backups, updates, monitoring, implementing CDNs, securing your CMS, and choosing an appropriate hosting solution aligned with technical requirements and goals. Strapi offers an Enterprise Edition suitable for larger businesses, which can be self-hosted or cloud-hosted, providing advanced features like RBAC, SSO, enhanced security, dedicated support, professional services, and multi-environments.
Oct 06, 2024
1,717 words in the original blog post.
Strapi is a headless content management system (CMS) that offers features tailored for eCommerce businesses, helping them improve their search engine optimization (SEO). By using Strapi's SEO-friendly URL structure and customizable metadata fields, online stores can optimize their websites for search engines. Additionally, integrating plugins like Algolia Search, sitemap generation, and JSON-LD structured data can further enhance site performance and visibility. To maximize the benefits of Strapi, eCommerce businesses should also focus on fast loading pages, mobile responsiveness, valuable content creation, schema markup implementation, descriptive meta tags, clear site structure, localization strategies, and SEO tool integration.
Oct 05, 2024
542 words in the original blog post.
Content management systems (CMS) are vital for building and managing websites and applications, handling sensitive data and supporting important online services. Strapi is an open-source headless CMS built with JavaScript and TypeScript that integrates well with various front-end frameworks as an API-driven CMS. Its security features include strong default practices to reduce vulnerabilities such as SQL injection and cross-site scripting (XSS). WordPress, a popular CMS, offers basic security features often enhanced by plugins, but its dependence on third-party plugins can introduce vulnerabilities if not properly maintained. Implementing strong security measures is essential for CMS security, with Strapi's built-in features and modern architecture providing strong protection with less dependence on external plugins, reducing potential vulnerabilities. For organizations that prioritize strong security and flexibility, Strapi is a compelling option.
Oct 04, 2024
805 words in the original blog post.
The text discusses best practices for integrating Strapi, a popular open-source headless CMS, with other platforms and services to enhance application capabilities. It emphasizes the importance of securely handling sensitive information using environment variables, effectively utilizing APIs and webhooks, following security best practices when integrating with third-party services, optimizing performance and scalability, managing content and data, ensuring data integrity, implementing robust authentication and authorization, securing API endpoints, monitoring security events, deploying applications effectively, automating deployment processes, efficient debugging and error handling strategies, maintaining data consistency between Strapi and external systems, and optimizing performance during integrations. The text also highlights the benefits of using Strapi Cloud for a fully managed headless CMS solution.
Oct 04, 2024
1,330 words in the original blog post.
Strapi 5 introduces several new features and improvements to the leading open-source headless CMS. These include simplified API responses, introduction of documents for efficient grouping and management of content, use of documentId instead of id in API calls, enhanced TypeScript support, changes in creating entries while uploading files, new functions for Document Service API, and a new Plugin SDK for creating plugins. Additionally, Strapi 5 now uses Vite as its module bundler and has an AI-based chatbot on the documentation page.
Oct 03, 2024
2,117 words in the original blog post.
In this tutorial, we discussed how to migrate a Strapi v4 project to Strapi v5 using the upgrade tool via codemods. We also went over how to update our frontend to work with Strapi v5 and how to use the Strapi-Response-Format header to get the old Strapi v4 response format. This allows us to quickly migrate our front end to work with Strapi v5 without having to rewrite all our code. But, like any migration, this is a good time to refactor and consider what improvements you can make in the frontend.
Oct 01, 2024
4,227 words in the original blog post.
The text discusses various security plugins for Strapi applications to protect user data and enhance user experience. These include authentication plugins like Users & Permissions, Passwordless, GoogleAuth, and Auth0 integration. It also mentions the RBAC system in Strapi that allows custom roles with specific permissions. Additionally, it covers validation and sanitization functions such as sanitizeQuery, sanitizeInput, validateQuery, and validateInput. The text highlights rate limiting plugins like strapi-plugin-security to prevent abuse and enhance security by setting thresholds for requests. It also emphasizes the importance of logging and monitoring user activities and system events using Strapi's middleware and event system. Furthermore, it discusses the Backup plugin for data protection and recovery in case of loss or corruption. The text concludes with recommendations to keep Strapi applications secure by updating security plugins, customizing them according to specific needs, testing them in a staging environment, and regularly auditing security settings.
Oct 01, 2024
904 words in the original blog post.
The text provides a comprehensive guide on migrating from Contento to Strapi, an open-source headless CMS built with JavaScript. It covers understanding the differences between API-first and Git-based CMS approaches, preparing for migration, setting up Strapi, importing content, customizing Strapi, testing and launching the new CMS, and maintaining its performance post-migration. The guide emphasizes the benefits of using Strapi, such as complete control over content modeling and API customization, self-hosting capability, cost efficiency, and alignment with JavaScript development stack. It also provides step-by-step instructions on how to install Strapi, configure it, import data from Contento, and adjust settings to fit the organization's needs.
Oct 01, 2024
1,170 words in the original blog post.