Creating a Community Board with HTMX & Express
Published: 2024-03-28 08:55:01

---
title: Creating a Community Board with HTMX & Express
published: true
date: 2024-01-21 09:48:13 UTC
tags: 
canonical_url: https://www.tercmd.com/community-board-using-htmx-express
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ohpmfsko90vc5lanpnwn.png
---

## 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.

```sh
npm init -y
npm install express hbs --save
```

Create `index.js` with the following contents:

```js
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:

```html
<!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)

💡 Every time the `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:

![A navbar with three options for "Home", "About" and "Submit"](https://cdn.hashnode.com/res/hashnode/image/upload/v1705825983942/1c93f094-1b4a-4383-a8ad-7403918f6724.png)

## Step 2. Listing articles

Create a file in the `views` directory with the name `articles.hbs` with this content:

```hbs
{{#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.

```js
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:

```js
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.

```html
<div hx-get="/articles/list" hx-trigger="load" hx-swap="outerHTML" aria-busy="true">Loading articles...</div>
```

![Two articles showing on the page](https://cdn.hashnode.com/res/hashnode/image/upload/v1705827572426/b739be61-3b1e-4004-a1c6-1bae21168644.png)

## Step 3. About page

On the about page, we follow similar steps as the index page.

![About page example](https://cdn.hashnode.com/res/hashnode/image/upload/v1705827766373/4501deda-2195-42cf-94df-afef4db4724f.png)

## Step 4. Submitting posts

We create a file called `submit.hbs` in the `views` directory.

```html
<!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)

```js
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!`

```js
app.post('/submit', (req, res) => {
    articles.unshift({name: req.body.title, description: req.body.description});
    res.send('<p>Post submitted!</p>');
})
```

💡 As this is a simple response, I've not created a template for it. Also, this code is purely for adding to the beginning of the `articles` object. This should be replaced with database queries.

Then a user can submit posts.

![Submit form with title and description filled in](https://cdn.hashnode.com/res/hashnode/image/upload/v1705828652390/29809f60-8788-4b6e-ad3b-f171110fc755.png)

![Homepage showing new post](https://cdn.hashnode.com/res/hashnode/image/upload/v1705828552587/9f801211-cb3d-4ed5-9da0-f0102049e19c.png)

That's it! If you have any feedback, please leave a comment.