Melina.js
Streaming-First Web Framework
Build blazing fast web applications with zero configuration. Experience immediate Time to First Contentful Paint with our streaming-first architecture.
Melina.js Core Features

Streaming Performance
Melina.js delivers blazing fast performance with true streaming-first rendering. Your users see content instantly, even while data is still loading.

Smart Chunk Mapping
Automatic chunk mapping ensures only the code you need is loaded, optimizing both speed and resource usage for every page.

Modern Build Tooling
Enjoy a modern, zero-config build pipeline. Melina.js integrates seamlessly with your workflow for fast development and reliable production builds.

Built to run on Bun, a fast all in one JavaScript runtime, Melina.js leverages Bun's native bundler (which is up to 200x faster than Webpack) to offer a seamless development experience.
Unlike other frameworks such as Next.js, Melina.js eliminates the need for configuration.
Features
- 🚀Simplified SetupDefine a server handler and start building.
- 🌊Streaming by DefaultReturn AsyncGenerators from your handler for immediate Time to First Contentful Paint.
- 🧩Dynamic Import MapsGenerate modern ES module import maps from your package.json on the fly.
- ⚡On-Demand Asset BuildingClient-side JavaScript and CSS are built when requested during development, and cached in production.
- 📏Framework AgnosticWorks with React, Vue, Svelte, or vanilla JS on the client-side.
- 📊Built-in Performance MeasurementDebug and optimize with ease using the measure utility.
- 🔥Tailwind CSS JITSeamless Tailwind CSS integration for your assets.
How It Works
Melina.js simplifies web application delivery with a handler-centric approach:
When a request comes in, it's routed to the main handler function you provide to serve().
Your handler processes the request. You can implement routing, API endpoints, or page generation logic.
For HTML pages, your handler can return an AsyncGenerator<string>. Melina immediately starts streaming the first chunk of HTML to the browser, allowing instant parsing and rendering.
Use asset(filePath) in your server-side logic to get a URL for client-side JS or CSS.
Development: Assets are built on-the-fly by Bun.
Production: Assets are built once and served with long-cache headers.
Use imports([...dependencies]) to generate an import map from package.json. Inject this into your HTML stream for bare module specifiers.
Inject server-side data into the HTML stream by embedding a <script> tag (e.g., window.SERVER_DATA) for the client to pick up.
Quick Start
React with Tailwind CSS Example
- Install dependencies for the example:
bun add react react-dom react-client bun add -d @types/react @types/react-dom tailwindcss bun-plugin-tailwind
- Create your React App Component (
App.tsx
):// ./App.tsx import React from 'react'; // Make sure serverData is typed appropriately for your app // For this example, we expect { now: string } interface ServerData { now?: string; message?: string; } interface AppProps { serverData: ServerData; } const App: React.FC<AppProps> = ({ serverData }) => { return ( <div className="p-4"> <h1 className="text-2xl font-bold mb-2">Hello from Melina.js & React!</h1> <p className="text-lg">Data from server:</p> <pre className="bg-gray-100 p-3 rounded mt-1 text-sm"> {JSON.stringify(serverData, null, 2)} </pre> </div> ); }; export default App;
- Create a client-side entrypoint (
App.client.tsx
):// ./App.client.tsx import React from 'react'; import { createRoot } from "react-dom/client"; import App from './App'; declare global { interface Window { SERVER_DATA: any; } } const serverData = window.SERVER_DATA || { message: "No server data received" }; createRoot(document.getElementById("root")!).render( <React.StrictMode> <App serverData={serverData} /> </React.StrictMode> );
- Create your Tailwind CSS entrypoint (
App.css
):
*Ensure your/* ./App.css */ @import "tailwindcss" source("./");
tailwind.config.js
content
array points to your.tsx
files, e.g.,content: ["./*.{(html, js, jsx, ts, tsx)}"]
- Create your server file (
server.ts
):// server.ts import path from "path"; import { useServer, measure } from "melinajs"; const { serve, asset, imports } = useServer(); const generatedImportMaps = await measure( async () => imports(['react', 'react-dom/client', 'react/jsx-dev-runtime']), "Generate Import Maps" ); const importMapScript = ` <script type="importmap"> ${JSON.stringify(generatedImportMaps, null, 2)} </script> `; async function* streamReactPage(req: Request) { const requestId = req.headers.get("X-Request-ID") || "unknown"; yield ` <!DOCTYPE html> <html> <head> <title>Melina + React</title> ${importMapScript} <script src="${await asset( path.join(__dirname, "App.client.tsx") )}" type="module" defer></script> <link rel="stylesheet" href="${await asset( path.join(__dirname, "App.css") )}" /> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <div id="root"> <div class="p-4 text-xl text-gray-500">Loading app...</div> </div> `; // Simulate some async data fetching const serverData = await measure(async () => { await Bun.sleep(50); // Simulate delay return { now: new Date().toISOString(), message: "Data fetched on the server!" }; }, "Fetch Server Data", { requestId }); yield ` <script> window.SERVER_DATA = ${JSON.stringify(serverData)}; </script> </body> </html> `; } serve(async (req: Request) => { const url = new URL(req.url); if (url.pathname === '/') { return streamReactPage(req); } if (url.pathname === '/api/hello') { return Response.json({ message: "Hello from API" }); } return new Response('Not Found', { status: 404 }); }); console.log("React example server running. Open http://localhost:3000");
- Update
package.json
(ensuretype: "module"
):{ "name": "melina-react-example", "type": "module", "scripts": { "dev": "bun run server.ts", "start": "NODE_ENV=production bun run server.ts" }, "dependencies": { "melinajs": "latest", "react": "^18.2.0", "react-dom": "^18.2.0", "react-client": "latest" }, "devDependencies": { "@types/bun": "latest", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "bun-plugin-tailwind": "^0.0.15", "tailwindcss": "^3.3.0", "typescript": "^5.0.0" }, "peerDependencies": { "typescript": "^5" } }
- Start your server:
Openbun run dev
http://localhost:3000
in your browser.
Ready to Get Started?
Join thousands of developers building fast web applications with Melinajs