htmx is a small, free-dependency library that gives access to AJAX, CSS Transitions, WebSockets and Server Sent Events directly in HTML, using attributes.
-
Start a new project:
npm init -y
-
Install the dependencies:
npm i express handlebars express-handlebars dotenv npm i -D typescript @types/express @types/node ts-node-dev
-
Install Inline HTML extension in VSCode.
-
Update
package.jsoncontents:{ "name": "server", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "dev": "ts-node-dev --respawn --debug --deps --rs src/server.ts", "build": "tsc", "start": "node dist/server.js" }, "keywords": [], "author": "", "license": "ISC", "type": "commonjs", //... } -
Create
tsconfig.jsonin the project root folder with the following content:{ "compilerOptions": { "target": "ES6", "module": "CommonJS", "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "paths": { "@/views/*": [ "./src/views/*" ] }, }, "include": [ "src/**/*", "./typings.d.ts", "templates/**/*" ], "exclude": [ "node_modules" ], } -
Create
typings.d.tsin the project root folder with the following content:declare module "*.css"
-
Create
src/lib,src/views,templates/layout,public/cssandpublic/jsfolder in the root directory. -
Create
src/server.tsfile with the following content:// https://medium.com/@ayobamieae/a-detailed-guide-on-how-to-set-up-a-simple-server-with-typescript-72c1594e5692 // https://dev.to/shayy/create-templates-in-nodejs-with-handlebar-256c import express, { Request, Response } from 'express'; import dotenv from 'dotenv'; import { engine } from 'express-handlebars'; import path from 'path'; const templateDir = path.join(process.cwd(), "templates") // .env configuration dotenv.config(); const PORT = process.env.PORT || 8000; // create app const app = express(); app.use(express.urlencoded({ extended: false })); // handlebars setup app.engine('handlebars', engine({ // Here we define what format you will use (That means what's at the end of each file, for example test.handlebars or test.hbs) defaultLayout: 'main', // That's the name of your template file. In my case it's main.handlebars layoutsDir: path.join(templateDir, 'layouts') // That's the directory where the template file is })); app.set('views', templateDir); // Here you give express the information that it has to look at all files that are in the path /views app.set('view engine', 'handlebars'); // Here you say express that you are using handlebars to build your website app.enable('view cache'); // static assets app.use(express.static('public')); /* Routes to be added here*/ // listen to port app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });
-
Create
src/lib/template-utils.tswith the following content:import path from "path"; import fs from 'fs'; import Handlebars from 'handlebars'; import HandlebarsTemplateDelegate from 'handlebars'; const templateDir = path.join(process.cwd(), "templates") export function loadTemplate(page: string[], options?: { [x: string]: unknown }): HandlebarsTemplateDelegate<unknown> { const templateString = fs.readFileSync(path.join(templateDir, page) + ".handlebars", 'utf8'); return Handlebars.compile(templateString, options); }
-
Create
public/css/styles.csswith the required styles. -
Download htmx.min.js and save it as
public/js/htmx.min.js. -
Create
templates/layout/main.handlebarswith the following content:<html> <head> <title>Website Name</title> <link rel="stylesheet" href="/css/styles.css" /> <script src="/js/htmx.min.js"></script> </head> <body> {{{body}}} </body> </html>
Once the project is setup, create routes and their corresponding HTML files/templates. The htmx attributes can added to the HTMLs tags. As long as the main.html file that loads /js/htmx.min.js is used as the base, HTMX will work.
-
Add a new route to
server.ts:// landing page routes app.get('/route-name/:id', (req: Request, res: Response) => { const { id } = req.params; const data = extractData(id); res.render("index", { showTitle: true, param: data }); });
route-nameis the unique route name that is picked up by express-handlebars, which will look for a handlebars file with the same name in the templates folder. -
Add
templates/route-name.handlebarsfile with the following content:<header> <h1>Website</h1> </header> <main> <!-- ... --> <div> <p>{{param.title}}</p> <!-- ... --> </div> <!-- ... --> </main>
This file is transpiled by express-handlebars and then sent to the browser as landing page.
-
Add a new route to
server.ts:import * as route from './views/route'; // ... // landing page routes app.get('/route-name/:id', (req: Request, res: Response) => { const { id } = req.params; const data = extractData(id); res.send(route.default(data)); });
-
Create
src/views/route.tsfile with the following content:import { loadTemplate } from "../lib/template-utils"; import "./partial-page" const PAGE = "route" const template = loadTemplate([PAGE]) export default template;
-
Create
templates/route.handlebarsfile with the following content:<div> {{#each parameter}} {{>PartialPage param=.}} {{/each}} </div>
-
Create
src/views/partial-page.tsfile with the following content:import { loadTemplate } from "../lib/template-utils"; import Handlebars from "handlebars" const PAGE = "partial-page" const template = loadTemplate([PAGE]) Handlebars.registerPartial("PartialPage", template) export default template;
-
Create
templates/partial-page.handlebarsfile with the following content:
<div data-id="{{param.id}}">
<p>{{param.title}}</p>
<!-- ... -->
</div>-
Add a new route to
server.ts:import * as route from './views/route'; // ... // landing page routes app.get('/route-name/:id', (req: Request, res: Response) => { const { id } = req.params; const data = extractData(id); res.send(route.default(data)); });
-
Create
src/views/route.tsfile with the following content:const createBookTemplate = (data) => /*html*/` <li data-id="${data.id}"> <p>${data.title}</p> <!-- ... --> </li> `; export default createBookTemplate;