Skip to content

A lightweight TypeScript middleware for Express to seamlessly handle Hotwire Turbo requests. This middleware simplifies detecting Turbo Frame requests and sending Turbo Stream responses.

License

Notifications You must be signed in to change notification settings

Ucok23/express-hotwire-middleware

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Express Hotwire Middleware

A lightweight TypeScript middleware for Express to seamlessly handle Hotwire Turbo requests. This middleware simplifies detecting Turbo Frame requests and sending Turbo Stream responses.

Features

  • Turbo Detection: Automatically detects if a request is from a Turbo Frame or accepts Turbo Streams (req.isTurbo).
  • Frame Identification: Easily access the Turbo Frame ID (req.turboFrame).
  • Stream Responses: Typed helper to send Turbo Stream responses (res.sendTurboStream) with correct Content-Type.
  • TypeScript Support: First-class TypeScript support with extended Express definitions.

Installation

npm install express-hotwire-middleware

Usage

Setup

import express from 'express';
import { turboMiddleware } from 'express-hotwire-middleware';

const app = express();

// Apply the middleware
app.use(turboMiddleware);

Request Handling

Check if the request is a Turbo request or specifically from a Turbo Frame:

app.get('/feed', (req, res) => {
  if (req.isTurboFrame) {
    // The request came from <turbo-frame id="...">
    console.log(`Request from frame: ${req.turboFrame}`);
    res.render('partials/feed');
  } else {
    // Full page load
    res.render('layout', { content: 'feed' });
  }
});

Sending Turbo Streams

Use res.sendTurboStream to send one or multiple stream actions. The Content-Type text/vnd.turbo-stream.html is set automatically.

app.post('/todos', (req, res) => {
  const newTodo = { id: 1, text: 'Buy milk' };

  // Send a single stream action
  res.sendTurboStream({
    action: 'append',
    target: 'todo-list',
    html: `<li id="todo-${newTodo.id}">${newTodo.text}</li>`
  });
});

Batch Updates

You can send multiple actions in a single response:

app.post('/clear-completed', (req, res) => {
  res.sendTurboStream([
    { action: 'remove', target: 'todo-1' },
    { action: 'update', target: 'counts', html: '<span>0 items left</span>' },
    { action: 'replace', target: 'notification', html: '<div class="alert">Cleared!</div>' }
  ]);
});

API Reference

Request Extensions

The middleware extends Express.Request with:

  • req.isTurbo: boolean
    • true if Turbo-Frame header exists OR Accept header contains text/vnd.turbo-stream.html.
  • req.isTurboFrame: boolean
    • true if Turbo-Frame header is present.
  • req.turboFrame: string | undefined
    • The value of the Turbo-Frame header (the ID of the frame).

Response Extensions

The middleware extends Express.Response with:

  • res.sendTurboStream(message: TurboStreamMessage | TurboStreamMessage[])
    • Sends the formatted <turbo-stream> HTML.
    • Sets Content-Type: text/vnd.turbo-stream.html.

Types

TurboStreamAction

Supported actions for Turbo Streams:

  • 'append': Appends the content to the target element.
  • 'prepend': Prepends the content to the target element.
  • 'replace': Replaces the target element with the content.
  • 'update': Updates the content of the target element.
  • 'remove': Removes the target element.
  • 'after': Inserts the content after the target element.
  • 'before': Inserts the content before the target element.

TurboStreamMessage

interface TurboStreamMessage {
  action: TurboStreamAction;
  target: string; // The DOM ID of the target element
  html?: string;  // The HTML content (optional for 'remove' action)
}

License

MIT

About

A lightweight TypeScript middleware for Express to seamlessly handle Hotwire Turbo requests. This middleware simplifies detecting Turbo Frame requests and sending Turbo Stream responses.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published