A lightweight TypeScript middleware for Express to seamlessly handle Hotwire Turbo requests. This middleware simplifies detecting Turbo Frame requests and sending Turbo Stream responses.
- 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.
npm install express-hotwire-middlewareimport express from 'express';
import { turboMiddleware } from 'express-hotwire-middleware';
const app = express();
// Apply the middleware
app.use(turboMiddleware);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' });
}
});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>`
});
});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>' }
]);
});The middleware extends Express.Request with:
req.isTurbo: booleantrueifTurbo-Frameheader exists ORAcceptheader containstext/vnd.turbo-stream.html.
req.isTurboFrame: booleantrueifTurbo-Frameheader is present.
req.turboFrame: string | undefined- The value of the
Turbo-Frameheader (the ID of the frame).
- The value of the
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.
- Sends the formatted
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.
interface TurboStreamMessage {
action: TurboStreamAction;
target: string; // The DOM ID of the target element
html?: string; // The HTML content (optional for 'remove' action)
}MIT