Plugins are a powerful tool to customize and extend the functionality of your Strapi app. The Strapi Market is the official marketplace to find plugins comprising more than sixty (60) plugins to choose from. These plugins were developed by the Strapi team, technology and solution partners, and individual community members. You can find plugins that integrate Strapi with other tools, such as Sentry, Mux, and Moesif, and plugins that extend Strapi features, such as SEO, content versioning, comments moderation, internationalization, database configuration, sitemap, and more.
This tutorial aims to show how you can enhance functionality in your Strapi blog by making use of 3 plugins from the Strapi Market. You will use the official Strapi SEO plugin to make your content more SEO friendly, the Internationalization (i18n) plugin to help distribute the content in different languages, and the Comments plugin to moderate comments on your blog. You will start by creating a simple static blog with Strapi as the backend and Jekyll for the frontend. Then, you will install the plugins and show their use cases. At the end of this tutorial, you should know how to install and use plugins from the Strapi Market to power up your app.
Strapi is the leading open-source headless Content Management System (CMS) with over 45000 Github stars. It saves its users API development time through an advanced and feature rich API CMS. It makes it simpler to access content through provision of fast and secure APIs built in an efficient and scalable manner.
Strapi is open source; the entire codebase is available and maintained on Github by thousands of contributors. It’s highly customizable with a beautiful admin panel that anyone can use and customize to their taste. The API as well can be customized to one’s needs. Content management functionality can be extended through the use of plugins available through the Strapi Market.
The APIs you generate using Strapi can be consumed using any frontend client from vanilla HTML, CSS and Javascript sites to frontend frameworks like React, Vue, Angular etc. to mobile apps and even IoT devices using REST or GraphQL.
Another reason why Strapi is so compelling is that you can self-host it. That’s right, you can host on your own servers or any other hosting providers. These include cloud platforms such as 21YunBox, Render, Heroku, AWS, Azure and others or locally using Docker
Strapi Market is the official marketplace that lists all the plugins created by the Strapi team, technology and solution partners, as well as individual community members. It is the trusted place where people can access all the submitted plugins. Anyone is free to develop and submit a plugin to the marketplace. All submitted plugins go through a Review Process. The verified plugins are highlighted and listed on the marketplace so that every plugin creator’s work is showcased and acknowledged.
Jekyll is a static site generator (SSG). It takes text written in your favorite markup language and uses layouts to create a static website. You can tweak the site’s look and feel, URLs, the data displayed on the page, and more. Some of the features include permalinks, categories, pages, templates and custom layouts.
Jekyll was developed using the Ruby language and uses Liquid as its templating language. It is also the SSG powering Github Pages.
In this tutorial, we will build a simple static blog using Jekyll and Strapi and install and showcase the functionality of the following plugins:
To complete the tutorial, the following prerequisites need to be installed:
Node.js: Node.js is the server-side runtime environment used to run our Strapi application. Only LTS versions are supported (minimum v12). You can download from the Downloads | Node.js page and install it using the provided instructions. To check if Node.js has been installed correctly, open up your command line and type node -v
.
npm (minimum v6) or yarn: This is the package manager for Node.js to run the CLI scripts. We will use this to install Strapi. npm comes bundled with Node.js. To check your version, enter npm -v
in your shell. If you prefer yarn, follow the instructions on the Installation | Yarn - Package Manager page to install it. To check if yarn is installed, type yarn -v
in your shell after installation.
ruby -v
)NOTE: Knowledge of the Ruby language and the Liquid templating language is not necessary but can be helpful to understand and complete the tutorial.
You can find the instructions on how to install Jekyll on the installation page for the Jekyll website.
The initial setup for the project involves installing and setting up Strapi as the backend for your Jekyll blog. It will host the content as well as the plugins to power up your blog.
node -v
. yarn create strapi-app jekyll-blog-backend --quickstart
The strapi server should be running automatically on port 1337. Go to http://localhost:1337/admin in your browser to set up your admin for Strapi.
Add an admin user to your Strapi app from the registration page.
Select Content-Type Builder
from the Strapi Dashboard sidebar to open up the Content-Type Builder page.
In this stage,
Collection Type
for posts. Create new Collection Type
and enter a post name. You can name it Post
. Advanced Settings
and disable the Drafts System
then click Continue
.Text
field with name title
and type short text
.Text
field with the name description
and type long text
.Rich Text
field named content
.Click Save
and wait for Strapi to restart. Once Strapi has restarted, "Post" is listed under Content Manager > Collection types in the navigation.
If you get stuck, refer to the Strapi Quick Start Guide, for detailed instructions.
Let's add a relation between Post
and User
from the users-permissions
plugin. This makes it easier to link a post to a user to display key data like the Author's name and profile picture, posts etc.
Relation
field to the Post
content type. The column on the left should be Post
, and the column on the right should be User (from: users-permissions)
. Finish
.Click Save
and wait for Strapi to restart for the changes to appear.
Next, we will add a dynamic auto-generated slug to the Post
collection.
Slug
and set the attached field to title
. Save
and wait for Strapi to restart for the changes to take effect.Slug
field. Edit Slug
menu set Editable Field to TRUE
. Now, the slug will be the same as the title for a blog article.
Let’s create new entries for our blog posts. 1. Create an entry for the “Post” collection type. 2. Go to Content Manager > Collection types - Post. 3. Click on Create new entry. 4. Add the title, description and content to your blog entry. 5. Click Save.
Repeat these steps to add multiple blog entries. Feel free to enter any content of your choice. Go to Content Manager > Collection types - Post to see a list of your blog entries.
For security reasons, the API access is restricted by default. We need to make the content available publicly through the API.
Now, test the link to the API by visiting http://localhost:1337/api/posts. If it’s working you should see an output showing the raw posts data as seen below.
Great job, your API is now working. With that, the backend is all set.
The next step is to set up your frontend for the blog. You will install Jekyll CLI and all its prerequisites using the instructions from the Jekyll installation page.
Next, install the jekyll bundler and gems using the following command:
$ gem install jekyll bundler
In this stage, we will generate our Jekyll project.
./jekyll-blog
or any other name you prefer to use. $ jekyll new jekyll-blog
To start the development server,
$ cd jekyll-blog
$ bundle exec jekyll serve
⚠️ WARNING: For Ruby version 3.0.0 or higher,
bundle exec jekyll serve
may not work. You can fix it by addingwebrick
to your dependencies:bundle add webrick
. See image below.
NOTE: Add the
--livereload
option toserve
to automatically refresh the page with each change you make to the source files:bundle exec jekyll serve --livereload
.
Data for statically generated website can come from varying sources. This can be markdown files, XML files, JSON, CSV, etc. The default data source for Jekyll is markdown. Fortunately, the data source which is the Strapi REST API is already in markdown format. We need to construct a simple Ruby script to consume the API.
In your Jekyll project directory, create a new ruby file and give it any name you want, I'll name mine jekapi.rb
. Copy and paste the following source code into jekapi.rb
.
1 # Ruby script to consume the Strapi API endpoint
2 require 'net/http'
3 require 'json'
4 require 'ostruct'
5
6 url = 'http://localhost:1337/api/post?populate=*'
7 uri = URI(url)
8 response = Net::HTTP.get(uri)
9
10 result = JSON.parse(response, object_class: OpenStruct)
11
12 result.data.each do |document|
13 url = 'http://localhost:1337/api/posts?populate=*'
14 uri = URI(url)
15 response = Net::HTTP.get(uri)
16
17 result = JSON.parse(response, object_class: OpenStruct)
18
19 result.data.each do |document|
20 post_file_name_suffix = document.attributes.Slug
21 post_file_name_prefix = document.attributes.createdAt
22 post_file_name_prefix.slice!(10..24)
23 post_file_name = "#{post_file_name_prefix}-#{post_file_name_suffix}"
24
25 post_file_title = document.attributes.title
26
27 post_file_date = document.attributes.createdAt
28
29 post_file_description = document.attributes.description
30
31 post_file_author = document.attributes.author.data.attributes.username
32
33 post_file_content = document.attributes.content
34
35 post_file = File.new("_posts/#{post_file_name}.md", "w")
36 post_file.puts("---")
37 post_file.puts("layout: post")
38 post_file.puts("title: \"#{post_file_title}\"")
39 post_file.puts("date: #{post_file_date}")
40 post_file.puts("description: #{post_file_description}")
41 post_file.puts("author: #{post_file_author}")
42 post_file.puts("---")
43 post_file.puts("#{post_file_content}")
44 end
Run the script below in your terminal:
$ ruby jekapi.rb
Your _posts folder should have your Strapi posts with extension *.md.
This Ruby script will fetch the blog posts from the api endpoint http://localhost:1337/api/post?populate=*. Requesting using this URL with a * wildcard query will populate all the fields defined in our Strapi API. Refer to the Strapi Documentation - REST API: Population & Field Selection for further details.
The script loops through all the JSON data and creates posts which are added to the _posts folder using the Jekyll post naming convention.
The next step is to display the list of articles for our blog.
$ bundle exec jekyll serve
The Jekyll site at http://localhost:4000 should now be displaying a list of our Strapi posts on the homepage:
Now, we have a fully functional blog. You can read any post by clicking the link in the home page. Each post will merge with the theme settings.
All the data from the Strapi API has been successfully retrieved and displayed. This includes the title
, author
, date
, description
and content
.
Now that we have set up our blog, let's power it up with some plugins
The next stage involves setting up our various plugins. We'll take them one after the other. Let's begin!
Internationalization is the process of developing your software so it can be localized to a particular audience that may vary based on culture, language, or region. It could mean removing all the hard-coded strings in your application and putting them in a JSON file. It is a cool feature you can add to your blog to cover a much wider audience.
The Strapi Internationalization (i18n) Plugin allows Strapi users to create, manage and distribute translated content in multiple languages.
This plugin was developed in house by the Strapi Team. Its features include the following:
Strapi v4 comes with i18n plugin by default. The other way is through NPM
. To install this plugin, you need to add an NPM dependency to your Strapi application:
# Using Yarn
yarn strapi install i18n
# Or using NPM
npm run strapi install i18n
A STRAPI_PLUGIN_I18N_INIT_LOCALE_CODE
environment variable can be configured to set the default locale for your environment. The plugin uses an ISO country code as the value for the environment variable.
This is useful when a Strapi application is deployed in a production environment, with the i18n plugin enabled
Our blog is using Strapi as a REST API endpoint. The i18n plugin adds new features to the REST API:
locale
parameter to fetch content for a specified localeOur blog will fetch localized entries with the locale parameter. The response to requests will include a string field locale
for the locale code for the content entry and an object localizations
which contains a data array of the id
and attributes
of the localization.
Go back to your Strapi Admin Dashboard and make sure that internationalization is enabled. Navigate to General > Plugins and make sure that internationalization is seen on the list of installed plugins:
If you face any issues with installation, refer to the Strapi i18n plugin documentation. Let's set up a new locale for our blog. Proceed to Settings > Internationalization and click on "Add new locale". For this tutorial, choose French (fr):
We need to enable localization for the Post collection. Navigate to Content-Type Builder > Post, click on Edit. Inside the pop-up, select Advanced Settings and check the box labeled Enable localization for this Content-Type. Wait for your server to restart for the changes to take effect:
Next, add translations to your blog posts. Go to Content Manager > COLLECTION TYPES > Post. Select one of your blog post entries. You should see the INTERNATIONALIZATION tab in the side panel with a Locales drop-down:
Click on the Locales drop-down and create content for your locale. After selecting your second locale, you’ll be taken to a new page to enter your translated content:
Once you’ve created your content, click Save. Repeat the same procedure for the other posts and you should have a few published entries with content available in multiple locales:
At the frontend, we need a way to consume the new locale data for our blog. Update jekapi.rb
with the locale data from the Strapi API endpoint:
1 #jekapi.rb
2
3 require 'net/http'
4 require 'json'
5 require 'ostruct'
6
7 url = 'http://localhost:1337/api/posts?populate=*'
8 uri = URI(url)
9 response = Net::HTTP.get(uri)
10 result = JSON.parse(response, object_class: OpenStruct)
11 result.data.each do |document|
12 post_file_locale_def = document.attributes.locale
13 post_file_name_suffix = document.attributes.Slug
14 post_file_date = document.attributes.createdAt
15 post_file_name_prefix = document.attributes.createdAt
16 post_file_name_prefix.slice!(10..24)
17 post_file_name = "#{post_file_name_prefix}-#{post_file_name_suffix}"
18 post_file_title = document.attributes.title
19 post_file_description = document.attributes.description
20 post_file_author = document.attributes.author.data.attributes.username
21 post_file_content = document.attributes.content
22
23 post_file = File.new("_posts/#{post_file_name}.md", "w")
24 post_file.puts("---")
25 post_file.puts("layout: post")
26 post_file.puts("title: \"#{post_file_title}\"")
27 post_file.puts("date: #{post_file_date}")
28 post_file.puts("description: #{post_file_description}")
29 post_file.puts("author: #{post_file_author}")
30 post_file.puts("locale: #{post_file_locale_def}")
31 post_file.puts("---")
32 post_file.puts("#{post_file_content}")
33
34 post_file_locale = document.attributes.localizations.data[0].attributes.locale
35 post_file_title_locale = document.attributes.localizations.data[0].attributes.title
36 post_file_description_locale = document.attributes.localizations.data[0].attributes.description
37 post_file_content_locale = document.attributes.localizations.data[0].attributes.content
38
39 post_file_alt = File.new("fr/_posts/#{post_file_name}.md", "w")
40 post_file_alt.puts("---")
41 post_file_alt.puts("layout: post")
42 post_file_alt.puts("title: \"#{post_file_title_locale}\"")
43 post_file_alt.puts("date: #{post_file_date}")
44 post_file_alt.puts("description: #{post_file_description_locale}")
45 post_file_alt.puts("author: #{post_file_author}")
46 post_file_alt.puts("locale: #{post_file_locale}")
47 post_file_alt.puts("---")
48 post_file_alt.puts("#{post_file_content_locale}")
49 end
Create a folder for your French content in your Jekyll working directory. Name the folder fr to match with the locale: fr
configured in the Strapi dashboard. Within fr, create a directory named _posts.
Run the jekapi.rb
script, then launch your Jekyll site. Open one of the posts and add a /fr/
prefix to the site link to view the French version of your post. There you have it: a simple implementation of internationalization in your website:
What's a good blog without some SEO?. The Strapi Team developed the Strapi plugin SEO to help with that.
metatitle
and metadescription
.Install the SEO plugin by adding it as an NPM dependency to your Strapi backend application.
# Using Yarn
yarn add @strapi/plugin-seo
# Or using NPM
npm install @strapi/plugin-seo
After installing, navigate to ./config/plugins.js
in your Strapi backend and add:
1 module.exports = ({ env }) => ({
2 // ...
3 seo: {
4 enabled: true,
5 },
6 // ...
7 });
Build your admin panel, then start your Strapi server:
$ yarn build
$ yarn develop
Your Strapi dashboard should now have SEO listed under Plugins:
Now that we have learnt how to install and configure it, here's how to use it:
The seo
component should now be listed under the Post collection type in the Content-Type Builder and if you check the SEO plugin menu you will find Post collection type has been added:
Now for the fun stuff. Select one of your posts in the Content Manager. The SEO Plugin menu should be available in the right side section of your post. It contains Browser Preview, Social Preview and SEO Summary for your blog post. Below the Slug text box is the seo component for you to add the necessary SEO entries for your blog post:
Click on+
in the seo box. In the pop-up you will see numerous seo entries to fill for your post. Add metaTitle, metaDescription, metaImage and keywords entries for your post, select + Add an entry then click Save:
The SEO Summary will give you hints together with the SEO Analyzer to improve your SEO entry performances.
The SEO Analyzer provides more context for your SEO to boost your performance so that you can have an SEO friendly metatitle
and metadescription
in your blog.
For more in-depth uses for the Strapi SEO Plugin check out The first Strapi SEO plugin article.
Now that your blog has i18n and improved SEO, an external media management solution can enhance your blog. Visit Cloudinary.
Cloudinary is an image and video management solution for websites and mobile apps. It covers everything from image and video uploads, storage, manipulations, optimizations to delivery. Cloudinary can deliver a large amount of data through a fast Content Delivery Network (CDN). This frees your backend from performance bottlenecks and frees up resources on your server.
To install the cloudinary provider plugin:
SECURITY REMINDER Save your account credentials somewhere safe and keep them secret.
Ctrl + C
. 4. Install the Strapi Cloudinary Provider Plugin: # using yarn
yarn add @strapi/provider-upload-cloudinary
# using npm
npm install @strapi/provider-upload-cloudinary --save
a. Provider Configuration
Add the following to ./config/plugins.js
:
1 module.exports = ({ env }) => ({
2 // ...
3 upload: {
4 config: {
5 provider: 'cloudinary',
6 providerOptions: {
7 cloud_name: env('CLOUDINARY_NAME'),
8 api_key: env('CLOUDINARY_KEY'),
9 api_secret: env('CLOUDINARY_SECRET'),
10 },
11 actionOptions: {
12 upload: {},
13 uploadStream: {},
14 delete: {},
15 },
16 },
17 },
18 // ...
19 })
In the root directory of your Strapi blog backend, create a .env file and add the following variables and with their respective values. These can be found in your Cloudinary dashboard under Account Details:
1CLOUDINARY_NAME = cloudinary-name
2CLOUDINARY_KEY = cloudinary-key
3CLOUDINARY_SECRET = cloudinary-secret
Due to the default settings in the Strapi Security Middleware, you will need to modify the contentSecurityPolicy
settings to properly see thumbnail previews in the Media Library i.e. you will be able to preview images uploaded to Cloudinary in your Strapi Admin dashboard. You should replace strapi::security
string with the object below in ./config/middlewares.js
as explained in the middleware configuration documentation.
1 module.exports = [
2 // ...
3 {
4 name: 'strapi::security',
5 config: {
6 contentSecurityPolicy: {
7 useDefaults: true,
8 directives: {
9 'connect-src': ["'self'", 'https:'],
10 'img-src': ["'self'", 'data:', 'blob:', 'dl.airtable.com', 'res.cloudinary.com'],
11 'media-src': ["'self'", 'data:', 'blob:', 'dl.airtable.com', 'res.cloudinary.com'],
12 upgradeInsecureRequests: null,
13 },
14 },
15 },
16 },
17 // ...
18 ];
Restart your Strapi server to complete the configuration. After uploading an image, it will upload to Cloudinary.
To use the newly-installed plugin, 1. Upload an image to one of your posts in Post collection in the Content Manager. 2. Select Add new assets and then upload an image. 3. Tick the one you want and click on Finish.
Your blog post now has a link to your image hosted on the Cloudinary cloud provider.
Navigate to your Cloudinary Management Console - Media Library and you should see the images you uploaded appear there.
Next let's rebuild our Jekyll site to see the changes take effect. In your Jekyll frontend working directory stop the Jekyll server using Ctrl+C
if you haven't already. Next, in the root folder of your Jekyll blog, clean your site:
$ bundle exec jekyll clean
Run the jekapi.rb
script to update your posts, then start the Jekyll server
$ ruby jekapi.rb
$ bundle exec jekyll serve
Navigate to the post you have updated with an image. If you have configured your Cloudinary Upload Provider plugin correctly, an image should appear in your post and if you inspect the html
for the image, it should show the cloudinary url to the image.
Now your blog can benefit from faster performance loading images hosted on a CDN at the same time off loading some work for your Strapi backend server.
That's it. You have seen the power and ease of creating a Jekyll blog powered by Strapi as a headless CMS to store your content and provide the content securely through a REST API. You powered up your blog by adding Internationalization, SEO and Cloudinary as an external upload provider to help manage your media and improve loading speed of your media assets.
Next, you could look at a deployment strategy for your blog on a production server. See this Strapi deployment guide. You can also tinker with adding some more plugins to your blog by checking out the numerous options on the Strapi Market
I hope you were able to follow along. Here's the repo with the complete source code to test out the full working version of the blog.
Mark Munyaka is a freelance web developer and writer who loves problem-solving and testing out web technologies. You can follow him on Twitter @McMunyaka