I recently started a new project and decided it wouldn’t benefit as much from client-side javascript frameworks such as Vue, Angular or React. Mostly the app needed to render some small data without much interactivity.

As I took to express and looked at existing server side rendering I wanted something that was very lightweight and gave me the same functionality as javascript functional iterators and map-reduce.

To my dismay much of what I found implemented a custom non-html templating system or a handle-bar type system that had a lot of dependencies and didn’t allow for inline iterators.

Then I had a realization. Why not use template literals? No dependencies, no installation, no transpilation and I could use any javascript functionality directly within HTML. I’m unsure if this is a common thing but it’s fairly powerful.

Lets build an example TODO with it.

For this example we’ll use express but any http router will work just fine.

First, lets build up our renderer, for template literals our index.js would look like this:

const express = require('express');

const app = express(); app.listen(process.env.PORT || "9000", (err) => {

if(err) {

console.error(err);

process.exit(1);

}

app.use(express.static('./public/'));

app.get('/index.html', async(req, res, next) => {

let data = {"user":"Trevor"}; // Some dynamic data to display.

res.send(require('./views/index.html')(req, data));

});

});

And the view would be an html file thats required from javascript land: ./views/index.html :

module.exports = (req, data) => `

<!DOCTYPE html>

<html>

<head><title></title></head>

<body>

${data.user}

</body>

</html>

`;

Of course we can also add in a /views/header.html and /views/footer.html as partial views so if we add other pages we don’t repeat ourselves.

Lets add a new file header.html that looks like this:

module.exports = (req, data) => `

<!DOCTYPE html>

<html>

<head><title></title></head>

<body>

`;

and footer.html that looks like this:

module.exports = (req, data) => `

</body>

</html>

`;

and then we can update ./views/index.html :

module.exports = (req, data) => `

${require('./views/header.html')(req, data)}

<div>

${data.user}

</div>

${require('./views/footer.html')(req, data)}

`;

Now that we have both a header and footer imported lets try creating a minimal TODO app. First, lets add the routes and store into our express app and add a body parser:

const express = require('express');

const app = express();

const parser = require('body-parser'); app.use(parser.urlencoded({ extended: true }));

let todo = []; app.listen(process.env.PORT || "9000", (err) => {

if(err) {

console.error(err);

process.exit(1);

}

app.use(express.static('./public/'));

app.get('/', async(req, res, next) =>

res.send(require('./views/index.html')(req, {todo})));

app.post('/', async(req, res, next) => {

todo.push({...req.body});

res.redirect('/');

});

});

Let’s also update the index.html with a form in a dialog to add a new item:

module.exports = (req, data) => `

${require('./header.html')(req, data)}

<h1>Todo</h1>

<table>

${data.todo.map((item) => `

<tr><td>${item.description}</td></tr>

`).join('')}

</table>

<dialog>

<form method="post" action="/">

<label for="description">Description</label>

<input type="text" name="description" id="description">

<button type="submit">Add</button>

</form>

</dialog>

${require('./footer.html')(req, data)}

`;

At this point we now have a semi-functional TODO app, although we can only add items to a list. Lets add some more functionality like a finished state and the a button to complete a task.

Here’s the updated index.js :

const express = require('express');

const app = express();

const parser = require('body-parser');

app.use(parser.urlencoded({ extended: true }));

let todo = [];

app.listen(process.env.PORT || "9000", (err) => {

if(err) {

console.error(err);

process.exit(1);

}

app.use(express.static('./public/'));

app.get(['/tasks','/'], async(req, res, next) =>

res.send(require('./views/index.html')(req, {todo})));

app.post('/tasks', async(req, res, next) => {

todo.push({

...req.body,

id:todo.length,

created:new Date(),

finished:null

});

res.redirect('/tasks');

});

app.post('/tasks/:id/done', async(req, res, next) => {

todo[req.params.id].finished = new Date();

res.redirect('/tasks');

});

});

And the new updates for index.html:

module.exports = (req, data) => `

${require('./header.html')(req, data)}

<style>

.done { text-decoration: line-through; }

</style>

<h1>Todo</h1>

<table>

${data.todo.map((item) => `

<tr>

<td class="${item.finished ? 'done' : ''}">

${item.created.toLocaleString()}

</td>

<td class="${item.finished ? 'done' : ''}">

${item.description}

</td>

<td>

${item.finished === null ? `

<form method="post" action="/tasks/${item.id}/done">

<button type="submit">Done</button>

</form>

` : ``}

</td>

</tr>

`).join('')}

</table>

<dialog open>

<form method="post" action="/tasks">

<label for="description">Description</label>

<input

type="text"

required

title="Enter a description to continue"

maxlength="1024"

name="description"

id="description" />

<button type="submit">Add</button>

</form>

</dialog>

${require('./footer.html')(req, data)}

`;



And that's it! A TODO app using server side rendering and template literals. Of course we could add in a css file for styling and maybe add a few extra frills like sorting and the finished date but overall I hope this demonstates the concept.