Templates

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:

section

Stylesheet

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.

Styles
/* 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!

section

Layout Template

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.

Layout

<!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">&copy;{{ 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.

  • <!DOCTYPE html> - This instructs the browser to render this page using HTML5 standards;
  • <html lang="{{ craft.app.language }}"> - The opening <html> tag is there to show the beginning of this HTML document. The part inside the curly braces dynamically sets the language of the document using a variable from Craft CMS that returns the current language of the application;
  • The <head> Section - This section sits at the top of your pages and will appear on each one. That is where you can put your website's name, a navigation bar, or any other elements that you want to be persistent throughout the website;
    • <meta charset="utf-8"/> - This tag sets character encoding to utf-8 which supports almost all characters;
    • <title>{{ siteName }}</title> - The <title> tag is what sets the title of the web page. It is what appears in the browser tab. The variable in the curly braces is the Twig variable that Craft replaces with the actual value. If you remember, when we first installed Craft, we gave it a name. That is the name that will appear here;
    • <meta content= "width=device-width, initial-scale=1.0" name= "viewport"> - This basically instructs the browser to adjust the width of the website to fit the screen size it is being viewed on. It makes the page responsive. The scale is the zoom level, with 1 representing no zoom;
    • {% do craft.app.view.registerCssFile('@web/styles.css') %} - This final bit of code references the styles.css file we created earlier, ensuring that particular style applies to this page;
  • The <body> Section - This section contains its own header that sits at the top of the page and displays the website's name. Like with the <title> tags from above, this takes the name we specified during the Craft installation;
  • The <main> Section - This is where the content of your website will go, as the code itself suggests. The {% block content %} and {% endblock %} tags define a Twig block. In this case, a content block. This way, different pages can define their own content inside this block. That saves on having to create a separate template for each page;
    • The "Nothing here, yet!" part is merely a comment that will not get rendered in HTML;
  • The <footer> Section - Finally, this part contains all the persistent content at the bottom of your pages.
    • The first <div> tag dynamically inserts the current year and the site name along with a copyright notice;
    • The second <div> tag adds a colophon in the footer that attributes the site to being built with Craft CMS. It also adds a link to Craft CMS that opens into a new tab.

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.

section

Index Template

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.

Index

{% 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.

  • Base Layout - The first line "extends" the _layout.twig file we created earlier. Extending means Craft will refer to that file for the layout code of this page. That way, we do not have to include that code in the index.twig file;
  • Fetching Posts - The next line tells Craft to retrieve all blog posts from the Blog Section we created earlier. They are stored in the posts variable, allowing for the CMS to dynamically display content based on the Entries we create in the Blog Section;
  • Displaying Content - The rest of the code (from {% block content %} to {% endblock %}) works towards displaying the actual content of those Entries. We have a <h1> tag that sets up a Blog title for the page, and below it is code that fetches posts from the posts variable and displays them individually. What is fetched from each post is the featured image, the blog post's title, publication date, summary, and a Continue Reading link is also available. They are all wrapped in a <article> tag, making them easier to understand by SEO bots but also helping structure the code better.

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!

section

Entry Template

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.

Entry

{% 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!

On this page...