Drawing of young person sitting on the floor with a laptop, surrounded by tall stacks of paper.

What’s a Content Management System?

As your web pro­jects grow, writ­ing plain HTML be­comes im­prac­ti­cal — a Content Management System (CMS) is the layer of ab­strac­tion you need.

Who this ar­ti­cle is for

This was orig­i­nally writ­ten as a sem­i­nar for un­der­grad­u­ate de­sign stu­dents, but it’ll work for any­one who is com­fort­able with HTML. If you’ve built a few web­sites in HTML and CSS and are ready to take on big­ger pro­jects, read on.

The Problem

Your first few web­sites are prob­a­bly built in plain HTML (and CSS and Javascript, but we’re not re­ally talk­ing about those here). There’s noth­ing wrong with those tech­nolo­gies — they’ll get you pretty far! But as your pro­jects grow, you tend to run into two prob­lems:

You’ll en­counter other is­sues when scal­ing up your web pro­jects, but many of them can be traced back to one of these two.

The so­lu­tion

The so­lu­tion to in­tro­duce a level of ab­strac­tion. Specifically, we’re go­ing to ab­stract our con­tent away from our markup (ie. our HTML), so we can work on each sep­a­rately. We do that in three steps:

  1. Take all the con­tent (like text and im­ages) out of our HTML file and put them into a sep­a­rate data­s­tore.
  2. Write tem­plates that look more or less like HTML but have spe­cial place­hold­ers where our con­tent used to be.
  3. Set up a piece of soft­ware that takes our con­tent and our tem­plates and com­bines them back into reg­u­lar HTML - be­cause that’s the only thing browsers un­der­stand.

The com­bi­na­tion of one, two, or all three of these things is called a CMS (Content Management System). Some CMS have even more fea­tures, like an in­ter­face to let you edit con­tent in the data­s­tore, or tem­plate cus­tomi­sa­tion, or an­a­lyt­ics, or web­host­ing — but the big, cen­tral idea is ab­stract­ing con­tent from markup.

The best way to un­der­stand this idea is to look at an ex­am­ple. I’ll leave out most of the tech­ni­cal de­tails for now - we’ll deal with them in the sec­ond part: Content Management Systems in the Real World.

An ex­am­ple

Let’s say we have a web­site called Max’s recipe box that lists a bunch of recipes and how long they take to cook. The site works great, but we’ve run into the two prob­lems we men­tioned in the be­gin­ning: We’re adding lots of recipes, so the HTML file is be­com­ing un­wieldy. Also, our friend Alice wants to con­tribute to the site, but she does­n’t want to edit HTML files. We’ve de­cided to ad­dress these prob­lems by get­ting the site onto a CMS. How do we go about that?

At the mo­ment, our HTML file looks like this:

<h1>Max's recipe box</h1>
<ul>
<li>
<h2>Mushroom pizza</h2>
<span>Duration: 0:45</span>
</li>
<li>
<h2>Pumpkin soup</h2>
<span>Duration: 0:45</span>
</li>
<li>
<h2>Apple pie</h2>
<span>Duration: 0:45</span>
</li>
</ul>

Let’s start by ex­tract­ing the ti­tle of our site (Max's recipe box) into a data­s­tore - in this case we’ll use a text file:

site_title
Max's recipe box

We have to la­bel the piece of data we ex­tracted so we can ref­er­ence it later. I came up with site_title, but any­thing that makes sense in your mind will work.

Then, we put a place­holder where that piece of con­tent used to be in our HTML. We’ll use a tem­plat­ing lan­guage called Liquid for these ex­am­ples, which uses {{ double curly braces }} to mark place­hold­ers - other lan­guages have dif­fer­ent con­ven­tions. Our file now looks like this:

<h1>{{ site_title }}</h1>
<ul>
<li>
<h2>Mushroom pizza</h2>
<span>Duration: 0:45</span>
</li>
<li>
<h2>Pumpkin soup</h2>
<span>Duration: 0:45</span>
</li>
<li>
<h2>Apple pie</h2>
<span>Duration: 0:45</span>
</li>
</ul>

Note that we’re us­ing the la­bel from our data­s­tore (site_title) to re­fer to the piece of con­tent we just ex­tracted. The ad­di­tion of that place­holder turns our HTML file into a tem­plate.

Our new setup is al­ready use­ful: If Alice wanted to change the ti­tle of the site, she would­n’t have to touch any HTML - all she would have to edit is that lit­tle text file.

Now, let’s do the same with the list of recipes. We start by pulling the ti­tles and du­ra­tions into an­other text file:

title, duration
Mushroom pizza, 0:45
Pumpkin Soup, 1:20
Apple Pie, 2:00

Again, we’re us­ing the first line of our file to la­bel our data: title and duration. Every line af­ter that rep­re­sents an in­di­vid­ual recipe, each with the ac­tual ti­tle and du­ra­tion. This way of or­gan­is­ing a text file is called CSV (Comma-Separated Values), and when you squint at it you’ll see that it works like a spread­sheet: the first line of the file lists the col­umn ti­tles, then the data fol­lows row af­ter row. You can ac­tu­ally ex­port CSVs from most spread­sheet soft­ware, which can be pretty handy.

With our data ex­tracted and or­gan­ised, we can re­place the recipe list in our tem­plate with more place­hold­ers:

<h1>{{siteTitle}}</h1>
<ul>
{% for recipe in recipes %}
<li>
<h2>{{recipe.title}}</h2>
<span>Duration: {{recipe.duration}}</span>
</li>
{% endfor %}
</ul>

The line {% for recipe in recipes %} is telling the com­puter: Hey! For every recipe in our data­s­tore, re­peat what­ever markup fol­lows un­til you see {% endfor %}. Between those tags we use place­hold­ers like {{recipe.title}} to dis­play spe­cific pieces of in­for­ma­tion for the cur­rent recipe. Liquid has many more con­structs like this for deal­ing with data in smart ways — for ex­am­ple, we could out­put dif­fer­ent HTML if a recipe has a par­tic­u­larly long ti­tle, or no ti­tle at all — but the prin­ci­ple is the same.

Moving our recipes into a data­s­tore has the same ben­e­fit as ex­tract­ing the ti­tle: If Alice wants to add a recipe to the list, she can just edit the CSV file. Even bet­ter, she could im­port that file into Google Sheets, in­vite other peo­ple, set up a whole ed­i­to­r­ial process for adding recipes — as long as she ex­port a CSV file at the end, it would­n’t im­pact our work­flow at all.

But we’ve also solved our sec­ond prob­lem: The tem­plate does­n’t care if our site has 5 or 5,000 recipes - it’ll it­er­ate through them and out­put the HTML just the same. If we need to change any­thing about the markup, we just edit the tem­plate and the com­puter does all the bor­ing typ­ing for us.

Demo

The eas­i­est way to get a feel for these con­cepts is to work with them di­rectly. Here’s a cod­ing en­vi­ron­ment with the three files we dis­cussed: Two CSV files (the data­s­tore) and a Liquid tem­plate. All three are ed­itable. Press the but­ton be­low to smush them into a HTML file, then change the data, the tem­plate, or both, and ob­serve how the ren­dered HTML changes.

Don’t worry about how ex­actly our data and tem­plates are ren­dered into HTML in this demo (though feel free to look at the code if you’re cu­ri­ous) — the goal for now is to get you com­fort­able with the prin­ci­ple of sep­a­rat­ing con­tent from markup. Once you’ve achieved that, you’re ready to tackle the tricky busi­ness of set­ting up your own con­tent man­age­ment sys­tem and us­ing it for real-world pro­jects.

This ar­ti­cle also ap­peared on Medium.