Updated on Jan 23, 2025
The last part of putting our blog website together is to create a frontend for it! Unlike WordPress, Craft does not come with pre-made templates, so you will have to create your own. Fortunately, the Twig templating engine is easy to work with, and we have prepared a few example templates for you to use. They are tailored for the website we built earlier, but you should have no issue adjusting them to your needs as the language is pretty straightforward.
Templates In This Post:
In this tutorial's previous sections, we set up all the necessary backend elements for our blogging website: a Section, an Entry Type, a Category, and Fields. Each of these elements combines to give not only the structure of the website but its URLs as well. When creating the Section, we specified the URL for our blog website as /blog. That means when you go to the address you initially installed Craft under, you can go to /blog, which will take you to the blogging website.
We set it up like that to differentiate it for the purposes of this tutorial. Of course, you can assign it to any URL you want. So, before we proceed with the actual templates, this is what the website's homepage looks like.
It contains the title of the application at the top, then the title of the Section, and then two blog posts we have created. You can refer to our Entry section for instructions on creating those. As you can see, there is also a Continue Reading button that will take you to the individual blog post. So, with this said, how did we get to this point? Through the use of three different templates and one stylesheet, all work together.
You should consider getting a code editor since we will be working with code, though. Such a code editor will highlight Twig's syntax (which is based on PHP) and make it easier for you to read and differentiate various code blocks. Sublime Text is beginner-friendly and requires minimal setup. All you have to do is select the language you want from View > Syntax > PHP, and the program will do the rest.
Another option is Visual Studio Code. It is a bit more advanced but should still work well enough for your purpose. Finally, if you are our customer, the text editor in the File Manager, which comes with cPanel, is also excellent at highlighting syntax.
With all of that said, let us start with the stylesheet first because it will provide a good baseline for how the website should look. Open your Craft CMS installation and go to the /web directory. In it, create a styles.css file and paste the following code into it.
/* Variables */ :root { --color-base: #222; --color-highlight: #E5422B; --color-muted: #AAA; --layout-site-gutter: 2rem; --layout-site-section-spacer: 6rem; --layout-site-width: 40rem; --type-system-ui: system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; --type-technical: monospace; } /* Containers */ body { color: var(--color-base); font-family: var(--type-system-ui); line-height: 1.5; margin: 0; padding: var(--layout-site-gutter) max(var(--layout-site-gutter), calc((100vw - var(--layout-site-width)) / 2)); } header { display: flex; justify-content: space-between; margin-bottom: var(--layout-site-section-spacer); } main {} footer { font-size: 0.75em; margin-top: var(--layout-site-section-spacer); text-align: center; } /* Elements */ img { display: block; max-width: 100%; height: auto; } a { color: inherit; text-decoration: none; } time { color: var(--color-muted); font-family: var(--type-technical); } /* Header */ header .home { font-weight: bold; } /* Navigation */ nav {} nav ul { display: flex; list-style: none; margin: 0; padding: 0; } nav li { margin-left: var(--layout-site-gutter); } nav a { color: var(--color-muted); } nav a.active, nav a:hover { color: var(--color-base); } /* Footer */ footer a { text-decoration: underline; } footer .copyright, footer .colophon { color: var(--color-muted); } /* Blog */ article + article { margin-top: var(--layout-site-section-spacer); } article a { color: var(--color-highlight); text-decoration: underline; } .thumbnail {} /* Posts */ .topics { display: flex; list-style: none; margin: var(--layout-site-gutter) 0; padding: 0; } .topics li { margin-right: 4px; } .topics a { background-color: var(--color-highlight); border-radius: 2px; color: white; font-family: var(--type-technical); padding: 2px 8px; text-transform: uppercase; } .topics a:hover { background-color: var(--color-base); } .feature-image { margin-bottom: var(--layout-site-section-spacer); margin-left: calc(-1 * var(--layout-site-gutter)); margin-right: calc(-1 * var(--layout-site-gutter)); } .post-content {} .content-block { margin-bottom: var(--layout-site-gutter); } .content-block:last-child { margin-bottom: 0; } /* About */ .about {} .about .photo { border-radius: 100%; height: 180px; margin: 0 auto var(--layout-site-gutter) auto; overflow: hidden; width: 180px; }
It defines the visual styles for the website and standardizes colors, spacing, and fonts across it. It also controls layout, text formatting, spacing, and responsiveness for various elements like the header, footer, images, and navigation. Finally, it includes code for the Blog Section we created earlier and the Posts Entry Type to ensure consistency throughout the website. Having this file allows us to reference it in our templates instead of specifying each template's style separately.
Now that we have defined the styles for the entire website, it is time to create some templates!
The first template we must create is the foundation upon which all other templates will be built. We will use it to set up some of our website's global features: header, content, and footer. The header and footer will be static and appear on every page, but the content will change depending on which page on the website we open.
To start, go to your Craft CMS installation on your computer and open the /templates directory. Then, create a file called _layout.twig. We put an underscore in it to indicate it should not be accessible directly and not be used on its own. It serves as a building block for other templates to expand upon. You will see when we explain what the code does. Speaking of which, here it is. Copy this code into the file.
<!DOCTYPE html>
<html lang="{{ craft.app.language }}">
<head>
<meta charset="utf-8"/>
<title>{{ siteName }}</title>
<meta content="width=device-width, initial-scale=1.0" name="viewport">
{% do craft.app.view.registerCssFile('@web/styles.css') %}
</head>
<body>
<header>
<a class="home" href="{{ siteUrl }}">{{ siteName }}</a>
</header>
<main>
{% block content %}
{# Nothing here, yet! #}
{% endblock %}
</main>
<footer>
<div class="copyright">©{{ now | date('Y') }} {{ siteName }}</div>
<div class="colophon">Built with <a href="https://craftcms.com/" target="_blank">Craft CMS</a></div>
</footer>
</body>
</html>
For your convenience, we will break down each section of this code and explain what it does.
As you can see for yourself, reading this code is relatively easy, even if you have just a basic understanding of HTML. Now that we have set up this template, we can reference it in our other templates, saving us a lot of time in coding these parts each time we need them.
As you already probably know, a browser opens a website by sending a request to the website's server, and then that server returns a response based on the URL requested. If you go to "https://google.com," the server will respond with the URL's content, which your browser then renders, and you can see it.
Craft is no different and still requires requests and responses to function. But if you try going to your blog right now, you will only see an error page. That is because we do not have a template for the blog's homepage! But we also did not set up a template the first time we installed Craft and opened it in our browser! We, as users, did not, yes, but Craft does it on its own. If you go to your application's directory and open /templates, you will see an index.php file. That file contains a template that shows you the Craft CMS welcome page!
The CMS is set up to look in both the /web and /templates directories for any files or templates associated with the requested URL before giving up and showing an error message. That is why the index.php file opens from the /templates directory even though we did not request it specifically. Now that we have this information, let us create such a file for our blog website so that when we open it, we actually get a homepage!
Go to the /templates directory in your Craft application and create a new directory within it. Call it "blog." Within that directory, create a file called index.twig. We will use a Twig template instead of standard HTML for ease of use. Within the file, place this code.
{% extends '_layout' %}
{% set posts = craft.entries().section('blog').all() %}
{% block content %}
<h1>Blog</h1>
{% for post in posts %}
{% set image = post.featureImage.one() %}
<article>
{% if image %}
<div class="thumbnail">
{{ image.getImg() }}
</div>
{% endif %}
<h2>{{ post.title }}</h2>
<time datetime="{{ post.postDate | atom }}">{{ post.postDate | date }}</time>
{{ post.summary | md }}
<a href="{{ post.url }}">Continue Reading</a>
</article>
{% endfor %}
{% endblock %}
Even by looking at it, you can probably understand what it does! That is the beauty of Twig; it is super easy to read. Nonetheless, let us go through it section by section and explain what precisely this will do for our blog.
Since this code is a loop (as indicated by the for … in statement at the beginning), Craft will go through each entry in the posts variable and display it on the page using the same elements. There is no need to explain each line of code here because what they do is evident even at a glance.
With this done, however, we are only one step away from having a functional blog website!
The last piece to our puzzle is creating a template for the blog posts themselves. If we went to any of the blog posts (Entries) we created earlier, we would get an error page. We have no templates for the Entries. Therefore, Craft has no way of displaying them. Fortunately, creating one is very straightforward, and we will even give you a starter template to tinker with.
Firstly, go to the /templates directory in your Craft CMS application and then to the /blog directory we created earlier. You should have only one file within it called index.twig. Create a new file called _entry.twig. As a safety precaution, placing an underscore in the name makes the template file inaccessible via URL. When ready, put this code in it.
{% extends '_layout' %}
{# Load the attached feature image: #}
{% set featureImage = entry.featureImage.one() %}
{# Load attached topics: #}
{% set topics = entry.postCategories.all() %}
{# Load the image block: #}
{% set imageBlock = entry.imageBlock.all() %}
{# Load the post text: #}
{% set postText = entry.postText %}
{# Load the summary block: #}
{% set summary = entry.summary %}
{% block content %}
{# Display the post title: #}
<h1>{{ entry.title }}</h1>
{# Display the feature image if it exists: #}
{% if featureImage %}
<div class="feature-image">
{{ featureImage.getImg() }}
</div>
{% endif %}
{# Display the categories/topics if any: #}
{% if topics | length %}
<ul class="topics">
{% for topic in topics %}
<li>{{ topic.getLink() }}</li>
{% endfor %}
</ul>
{% endif %}
{# Display the summary block if it exists: #}
{% if summary %}
<div class="content-block summary">
<p>{{ summary }}</p>
</div>
{% endif %}
<div class="post-content">
{# Display the post text: #}
{% if postText %}
<div class="content-block text">
{{ postText | md }}
</div>
{% endif %}
{# Display the second image from the image block if there are images: #}
{% if imageBlock | length %}
<div class="content-block images">
{% for image in imageBlock %}
<div class="image">
{{ image.getImg() }}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endblock %}
That is a lot of code, but if you take just a minute to go through it, you will learn exactly what it does. Even if the comments (lines between the hash marks) were not there, you could still tell what each code section does. This time, we added the comments to show you what they look like and how you can use them to explain the purpose of code directly within the file itself.
As you can see, we are telling Craft to load all the resources we want initially. What resources do we want? The Fields we created earlier for our Entry Types! As you can see, we have all of them: Feature Image, Post Category, Image Block, Post Text, and Summary.
Please note, however, the order in which we have arranged each individual element’s code block. Things will appear in that order on the page when rendered. Fortunately, each element is clearly segmented from the rest, so if you want your summary to be at the bottom of the post, you simply need to move it.
Each block begins with {% if … %} and ends with {% endif %}. So, cut that block out and move it wherever you need it. Do not worry; if you miss a line, you can always revert the changes you made if the website breaks. Since it is a template, you cannot do any permanent damage unless you delete something and then save it.
And there you have it! You are all set! Now open your browser, enter your website address, and go to /blog. You should see the blog posts you created neatly arranged. Here is what it looks like for us.
With this, our basic introduction to Craft CMS is concluded. You now have a working foundation upon which to expand your Craft CMS knowledge. All that is left of you to do is delve deeper into the syntax and code of the Twig templating engine and Craft’s numerous other options and functionalities. Also, do not forget about the plugins Craft has. Each of them can be a wholly new and different functionality for your website. Our suggestion is to create a throw-away website and experiment at will. The best way to learn is through practice!