Creating a Community Board with HTMX & Express
In this article, I create a community board project using HTMX on the client, Express on the server and Handlebars for server-side templating.
Step 1. Basic app
We create a folder for our application. Then we cd
into the folder and run npm init -y
.
Then we install express
and hbs
using npm or any other Node package manager.
npm init -y
npm install express hbs --save
Create index.js
with the following contents:
const express = require('express');
const app = express();
const hbs = require('hbs');
const port = 3000;
app.set('view engine', 'hbs');
app.use(express.static('static'));
app.use(express.urlencoded({ extended: true })); // support encoded bodies
app.get('/', (req, res) => {
res.render("index");
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
})
Create two folders called views
and static
in the root of your application directory.
The
views
folder contains the templates used for rendering the application.The
static
folder contains static files like images, CSS, etc.
Create a file called index.hbs
in the views
folder with this content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Community Board</title>
<script src="https://unpkg.com/htmx.org"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1.5.11/css/pico.min.css">
</head>
<body class="container" hx-boost="true">
<nav>
<ul>
<li>Home</li>
<li><a href="/about">About</a></li>
<li><a href="/submit">Submit</a></li>
</ul>
</nav>
</body>
</html>
This loads htmx and PicoCSS (a classless styling framework) from a CDN. In production, this is not recommended and should be loaded from your origin, but for this demo it should be okay.
Start the server using node index.js
(this script is compatible with Bun too)
index.js
file is changed, you must restart the server with the above command. However, changing the templates does not require a server restart.If you load this page, you should see this:
Step 2. Listing articles
Create a file in the views
directory with the name articles.hbs
with this content:
{{#each articles}}
<article>
<h1>{{this.name}}</h1>
<p>{{this.description}}</p>
</article>
{{/each}}
Then we get the list of articles (using a database query). For this example, I am using a simple object to store articles, however a database could be added in later.
var articles = [
{
"name": "Live GitHub Status (for GNOME Wayland)",
"description": "I had this cool idea to show the currently focused application on my system as part of my GitHub status."
},
// ...
];
We define a path to return a rendered list of articles in our Express server:
app.get('/articles/list', (req, res) => {
res.render("articles", {articles: articles});
})
And in index.hbs
, we add a div
that will fetch the articles and swap the returned HTML into itself.
<div hx-get="/articles/list" hx-trigger="load" hx-swap="outerHTML" aria-busy="true">Loading articles...</div>
Step 3. About page
On the about page, we follow similar steps as the index page.
Step 4. Submitting posts
We create a file called submit.hbs
in the views
directory.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Submit a Post</title>
<script src="https://unpkg.com/htmx.org"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1.5.11/css/pico.min.css">
</head>
<body class="container" hx-boost="true">
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li>Submit</li>
</ul>
</nav>
<form action="/submit" hx-post="/submit" method="post">
<input type="text" name="title" placeholder="Title" />
<input type="text" name="description" placeholder="Description" />
<input type="submit" value="Submit" />
</form>
</body>
</html>
We add this page to a /submit
path (using a GET
request, POST
is used for the form action)
app.get('/submit', (req, res) => {
res.render("submit");
})
Then we configure a path on our server to add to the list of articles, returning a message Post submitted!
app.post('/submit', (req, res) => {
articles.unshift({name: req.body.title, description: req.body.description});
res.send('<p>Post submitted!</p>');
})
articles
object. This should be replaced with database queries.Then a user can submit posts.
That's it! If you have any feedback, please leave a comment. Click the ❤️ if you liked this.