An editorial website is a site that publishes written, visual, or audio content. The most popular editorial websites tend to be online magazines and newspapers. Just making and publishing some form of content may seem straightforward, but hosting, managing, and delivering is challenging. You need to have a user-friendly platform to create, organize, and store the content on. You also need to easily manage who has access to it, what structure it should take, how it should be made available to various external platforms, etc. This is where a content management system (CMS) can come in handy.
In this tutorial, you will learn how to create an editorial website using Strapi and vanilla JavaScript. Strapi is a headless CMS that helps you create, manage, and deliver content. It provides an admin panel where you can input and configure your content. Additionally, it supports data like rich text, images, video, audio, etc. It also exposes a customizable API that makes it easy for external sites to consume your content.
By the end of this tutorial, you should have learned how to:
To build this project, you will need:
npm i strapi -g
In this step, you will install the Strapi app and set up an administrator account on its admin panel. The Strapi app will be named editorial-strapi-app
. To create the Strapi app, switch directories to where you’d like the app to reside. Run the following command on your terminal:
strapi new editorial-strapi-app --quickstart
Running this command will create the Strapi app, install its dependencies, and start it. The --quickstart
flag will generate the Strapi app using Strapi’s quickstart system. The Strapi app will use SQLite as a database.
Once the installation completes, you will be redirected to the Strapi admin panel at http://localhost:1337/admin/auth/register-admin. Here you will have to create an administrator account before you can proceed. Fill the page and create an account.
After you sign up, you will be redirected to the admin panel. This is where you will add content, media, and content types. You can also manage Strapi app and plugin settings on the panel.
By the end of this step, you should have installed the Strapi app and created an administrator account. You should have also accessed the admin panel. In the next step, you will generate an article API.
To create an article on Strapi, you first need to set up a content type for it. In this step, you will not only create an article content type but generate a whole API for it. First, you need to decide what attributes the article content type will have. For this tutorial, it will have 5:
Field Name | Field Type | Field Description |
---|---|---|
Title | String | This is the title of the article. |
Cover Image | Media | This is the main image displayed for the article. |
Description | Text | This is a brief introduction of what the article is all about. |
Content | Rich Text | This is the actual content of the article in markdown format. |
Tags | Component | These are the tags associated with the article. |
To generate the article API, stop the running Strapi app on the command line. Then run this command:
1 mkdir -p components/tag && touch components/tag/tags.json
Running this command will create the tags
component which you will later use when creating the article
API. In components/tag/tags.json, add the following:
1 {
2 "collectionName": "components_tag_tags",
3 "info": {
4 "name": "Tags",
5 "icon": "tags",
6 "description": ""
7 },
8 "options": {},
9 "attributes": {
10 "name": {
11 "type": "string",
12 "required": true,
13 "unique": true
14 }
15 }
16 }
A tag will have one attribute: name
. Each tag name needs to be unique and non-empty, that’s why required
and unique
are set to true
.
Next you will generate the article
API. Run this command:
1 strapi generate:api article content:richtext title:string description:text
This command will generate an article
API. It will create the article model, its routes, controllers, and services. You’ll notice that a new folder has been added at api/article. This is where this API resides.
Running this command is limiting as you are unable to specify finer details for the attributes like whether its required. You will have to modify the attributes in the model settings file, at api/article/models/article.settings.json. Since it’s not possible to specify a media or component type with this command, you will also have to add the missing cover_image
and tags
attributes. In api/article/models/article.settings.json add the following:
1 {
2 "kind": "collectionType",
3 "collectionName": "articles",
4 "info": {
5 "name": "article",
6 "description": "Articles for the editorial site"
7 },
8 "options": {
9 "increments": true,
10 "timestamps": true,
11 "draftAndPublish": true
12 },
13 "pluginOptions": {},
14 "attributes": {
15 "content": {
16 "type": "richtext",
17 "required": true
18 },
19 "cover_image": {
20 "model": "file",
21 "via": "related",
22 "allowedTypes": [
23 "images"
24 ],
25 "plugin": "upload",
26 "required": true,
27 "pluginOptions": {}
28 },
29 "description": {
30 "type": "text",
31 "required": true
32 },
33 "tags": {
34 "type": "component",
35 "repeatable": true,
36 "component": "tag.tags"
37 },
38 "title": {
39 "type": "string",
40 "required": true,
41 "unique": true
42 }
43 }
44 }
Notice how most of the fields are now required and that the tags
and cover_image
attributes are now included in the attributes
. The tags
attribute is marked as repeatable
so you can add multiple tags to an article. Now you can start the Strapi app to confirm that the article
content type has been added. Do this by running:
npm run develop
In this step, you generated the articles
API. In the next one, you will make the routes created in this step public.
When you generate the article
API, six routes are created. These routes expose functionality to:
1- create an article
2- update an article
3- delete an article
4- return an article using its ID
5- return all articles
6- return a count of articles.
However, all these routes are made private and will return a 403
forbidden error when called. For this tutorial, you will need two routes: one that returns an article given its ID and another that returns a list of articles. These are the routes that you shall make public in this tutorial. Here’s how you will do this:
findone
and find
checkboxes.That’s it. Now the /articles
and /articles/:id
routes are accessible. However, since you haven’t added any sample articles yet, they won’t be able to return any data.
In this step, you made the articles
and articles/:id
routes public. In the next step, you shall add sample articles for the routes to return.
In this step, you will create sample articles that the public API routes will return. You will do this by:
Make at least three articles to use for your editorial site. Here’s what that form should look like.
In this step, you created at least three articles to display on your editorial website. In the next step, you will clone a starter editorial app and fetch the articles from the Strapi app.
The editorial site that will display the articles is made from HTML, CSS, and vanilla JavaScript. No framework will be used in it. In this step, you will clone a starter editorial app from Github that contains all the styling and HTML markup. All you will have to do in the subsequent steps is to add the vanilla JavaScript files to fetch the articles from Strapi.
Pick a directory where you want to put the editorial site. Run this command on your terminal:
1 git clone https://github.com/zaracooper/editorial-app.git
Once the cloning is complete, switch directories into editorial-app
.
1 cd editorial-app
The app has two branches: main
which is the complete app and starter
which is what you will build off of. Switch branches to starter
by running:
1 git checkout starter
Next, install the app’s dependencies.
npm install
The app uses a couple of dependencies: [lite-server](https://www.npmjs.com/package/lite-server)
to serve the app and [showdown](https://www.npmjs.com/package/showdown)
to convert markdown to HTML. These are what are installed above.
You can now run the server. Note that the scripts that fetch the articles from Strapi and populate the pages with them are still empty. So the pages will be mostly blank apart from a few titles. To run the app use:
npm run dev
Running the above command will launch the app in a browser at http://localhost:3000. When you make any changes, lite-server
will reload the open pages. Now you can start adding code to fetch the articles from Strapi.
In this step, you cloned a starter editorial app from GitHub, installed its dependencies, and launched it. In the proceeding step, you will fetch a list of sample articles you created from Strapi and display them on the home page.
The home page, index.html, displayed at http://localhost:3000 is pretty bare. During this step, you will fetch the articles from http://localhost:1337/articles and display them on the home page.
The script for the home page is located at scripts/index.js. It has two functions:
fetchArticles()
which fetches articles from the Strapi /articles
route.createArticleCard()
which creates a card on the home page for each article. Here’s the code for the scripts/index.js file. It is the script for the index.html page. Copy this code into the scripts/index.js file.
1 // scripts/index.js
2 function fetchArticles() {
3 const articlesReq = new Request('http://localhost:1337/articles');
4
5 fetch(articlesReq)
6 .then(response => response.json())
7 .then(articles => {
8 let articleList = document.getElementById("article-list");
9
10 articles.forEach(article => {
11 articleList.appendChild(createArticleCard(article));
12 });
13 });
14 }
15
16 function createArticleCard(article) {
17 let card = document.createElement("div");
18 card.classList.add("card");
19 card.onclick = () => {
20 window.location.replace(`/pages/article.html?id=${article.id}`)
21 };
22
23 let cardImage = document.createElement("img");
24 cardImage.classList.add("card-img");
25 cardImage.src = `http://localhost:1337${article.cover_image.formats.thumbnail.url}`;
26
27 let cardBody = document.createElement("div");
28 cardBody.classList.add("card-body");
29
30 let articleTitle = document.createElement("p");
31 articleTitle.classList.add("card-title");
32 articleTitle.innerHTML = article.title;
33
34 let articleDescription = document.createElement("div");
35 articleDescription.classList.add("card-description");
36 articleDescription.innerHTML = article.description;
37
38 let articleTags = document.createElement("div");
39 articleTags.classList.add("article-tag-cont");
40
41 let tag;
42
43 article.tags.forEach(tg => {
44 if (tg.name) {
45 tag = document.createElement("span")
46 tag.classList.add("article-tag");
47 tag.innerHTML = tg.name;
48
49 articleTags.appendChild(tag);
50 }
51 });
52
53 cardBody.append(articleTitle, articleDescription, articleTags);
54
55 card.append(cardImage, cardBody);
56
57 return card;
58 }
59
60 fetchArticles();
fetchArticles()
makes a request to http://localhost:1337 using the fetch API and gets a JSON list of available articles. It then gets the article-list
div element where all the cards are to be added. For each article in the list, it calls createArticleCard(article)
to create a card. It then adds the card to the article-list
div. fetchArticles()
is called when the file is loaded.
createArticleCard()
creates a card for each article. It adds the image, title, description, and tags to the card. It also makes the card clickable so that when it is selected, the user is redirected to the pages/article.html page with the article id
as a query parameter. The pages/article.html page displays the actual content of the article as you will see in the next step.
Here is a screenshot of what this page should look like.
In this step, you added code to the index page script to fetch articles from Strapi and populate them on the home page. In the step that follows, you will fetch a single article from Strapi and display it on the article page.
The last thing to do is display the content of an article. Using the id
passed as a query parameter, you will fetch the article from the Strapi app. You will then convert the markdown content to HTML and add it to the page.
Once an article card is clicked on the home page, the user is redirected to this link: http://localhost:3000/pages/article.html?id={id}
. The id
of the article on the Strapi app is provided as a query parameter. You will take that id
and use it in the request URL to the Strapi app.
The script for the article page, pages/article.html, is found at scripts/article.js. Copy the code below and add it to that file.
1 // scripts/article.js
2 function checkForArticle() {
3 const urlParams = new URLSearchParams(window.location.search);
4 const articleId = urlParams.get('id');
5
6 if (articleId) {
7 getArticle(articleId);
8 } else {
9 showMissingArticleMsg("An article can't be retrieved without an ID.");
10 }
11 }
12
13 function getArticle(id) {
14 const articleReq = new Request(`http://localhost:1337/articles/${id}`);
15
16 fetch(articleReq)
17 .then(resp => {
18 if (resp.ok) {
19 return resp.json();
20 } else {
21 throw new Error(resp.statusText);
22 }
23 })
24 .then(displayArticle)
25 .catch(showMissingArticleMsg);
26 }
27
28 function displayArticle(article) {
29 document.getElementById("article-img").src = `http://localhost:1337${article.cover_image.url}`;
30
31 document.getElementById("article-title").innerHTML = article.title;
32
33 document.getElementById("article-description").innerHTML = article.description;
34
35 document.getElementById("published_date").innerHTML = (new Date(article.published_at)).toDateString();
36
37 let articleTags = document.getElementById("article-tags");
38 let tag;
39
40 article.tags.forEach(tg => {
41 if (tg.name) {
42 tag = document.createElement("span")
43 tag.classList.add("article-tag");
44 tag.innerHTML = tg.name;
45
46 articleTags.appendChild(tag);
47 }
48 });
49
50 const showdown = window.showdown;
51 const converter = new showdown.Converter();
52 document.getElementById("article-content").innerHTML = converter.makeHtml(article.content);
53
54 document.getElementById("article-cont").style = "display: flex; display: -webkit-box; display: -ms-flexbox;";
55 }
56
57 function showMissingArticleMsg(msg) {
58 document.getElementById("not-found").style = "display: flex; display: -webkit-box; display: -ms-flexbox;";
59 document.getElementById("err-msg").innerHTML = msg;
60 }
61
62 checkForArticle();
This script has four functions. checkForArticle()
checks that an id has been provided in the URL. If there is one, it will call getArticle()
which fetches the article from Strapi. If not, it calls showMissingArticleMsg()
which displays an error message. The screenshot below shows what that looks like. checkForArticle()
is called when the file is loaded.
getArticle()
makes a request to the Strapi app for an article given its id
. It makes a request to the http://localhost:1337/articles/${id}
route using the fetch API. If successful, it calls displayArticle()
to show the contents of the article on the page. If not, it throws an error and showMissingArticleMsg()
will display the error similar to what is shown in the screenshot above.
displayArticle()
takes the contents of an article and puts them on the page. It displays the cover image, title, publishing date, tags, and the content. It does this by adding them to existing elements already on the page. The content received from Strapi is in markdown. You will use showdown
to convert it to HTML before adding it to the page.
showMissingArticleMsg()
displays an error message on the page whenever there is a problem fetching an article. It’s called several times by the other functions.
Here’s a screenshot of what this page looks like when an article is populated.
This page is available at http://localhost:3000/pages/article.html?id={id}
where id
is the ID of an existing article on the Strapi app.
This is the end. You’ve created and editorial website using Strapi and vanilla JavaScript.
In this tutorial, you installed a Strapi app and generated an article API. You then made a couple of routes of the articles API public and added sample articles to display. Lastly, you cloned a vanilla JavaScript editorial starter app and added scripts to fetch articles from the Strapi app and display them. If you’d like to add more features to the app, maybe consider including authors in the article and making the tags linkable.
Strapi is a great choice for publishing your editorial content. Not only does it support rich text but it provides a user-friendly interface on its admin panel to compose it. It also offers a customizable API that your editorial site can use to consume the content. To learn more about what else Strapi can offer, check out this overview of its features on its website. You can find the source code for this app on Github.
Zara is a software developer and technical writer, using React Native, React, Rails, Node, Golang, Angular and many more technologies.