Next.js integration for gremllm - serve LLM-optimized markdown versions of your web pages by adding ?gremllm to any URL.
This package enables your Next.js application to serve token-optimized markdown for LLM consumption. When a request comes in for /about?gremllm, it automatically:
- Detects the
?gremllmparameter in middleware - Fetches the page HTML (SSR/SSG)
- Strips LLM-irrelevant content (navigation, footers, ads, scripts, styles, etc.)
- Converts to clean, semantic markdown
- Returns optimized markdown (~50-80% token reduction vs raw HTML)
- π Zero config - Works out of the box with sensible defaults
- π― Query parameter based - Add
?gremllmto any URL - β‘ Native performance - FFI bindings to Rust library via koffi for fast conversion
- π Full markdown output - Clean, semantic markdown optimized for LLMs
- π§ Configurable - Customize which elements to strip
- π¦ Automatic binary downloads - Platform-specific binaries downloaded on install
- πͺ TypeScript - Full type safety included
npm install @gremllm/nextjs koffi
# or
yarn add @gremllm/nextjs koffiImportant:
koffimust be installed as a peer dependency for native module support.
Wrap your config with withGremllm:
// next.config.js
const { withGremllm } = require('@gremllm/nextjs');
const nextConfig = {
// Your existing Next.js config
};
module.exports = withGremllm(nextConfig, {
// Optional: customize elements to strip
elementsToStrip: ['header', 'footer', 'nav'],
debug: false,
});Create app/api/gremllm/route.ts and simply export the pre-built handler:
// app/api/gremllm/route.ts
export { GET } from '@gremllm/nextjs/route';The handler includes:
- β URL extraction from middleware headers
- β HTML fetching with proper User-Agent
- β Content-type validation
- β Markdown conversion using your config
- β Error handling
- β Cache headers
Create or update middleware.ts at your project root.
Option A: Simple (recommended for new projects)
Just export the pre-built middleware:
// middleware.ts
export { middleware } from '@gremllm/nextjs/middleware';Option B: Compose with existing middleware
If you already have custom middleware logic, use gremllmMiddleware:
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { gremllmMiddleware } from '@gremllm/nextjs/middleware';
export function middleware(request: NextRequest) {
// Try gremllm first
const gremllmResponse = gremllmMiddleware(request);
if (gremllmResponse) {
return gremllmResponse;
}
// Your custom middleware logic here
// e.g., authentication, redirects, etc.
return NextResponse.next();
}Create a app/layout.tsx or pages/_document.tsx file and add the following metadata to your pages:
import {Html, Head, Main, NextScript} from 'next/document'
import { llmInstructions } from '@gremllm/nextjs/metadata';
export default function Document() {
return (
<Html lang="en">
<Head>
<meta name="llm-instructions" content={llmInstructions} /> {* This is what you need to add *}
</Head>
<body>
<Main/>
<NextScript/>
</body>
</Html>
)
}This will tell llm's that the ?gremllm query parameter is available.
Visit any page with ?gremllm:
# Regular HTML
curl https://yoursite.com/about
# LLM-optimized markdown
curl https://yoursite.com/about?gremllmRequest: /about?gremllm
β
Middleware (Edge Runtime): Detects ?gremllm
β
Rewrite: /api/gremllm (with URL in header)
β
API Route (Node.js Runtime): Fetches HTML, converts to markdown
β
Native Library (koffi FFI): Fast Rust-based conversion
β
Response: Clean markdown (text/markdown)
Architecture Rationale:
- Middleware runs in Edge Runtime (can't use native modules)
- API Route runs in Node.js Runtime (can use koffi/native modules)
- This split architecture gives us both speed and native module support
Pass options to withGremllm:
// next.config.js
module.exports = withGremllm(nextConfig, {
elementsToStrip: ['header', 'footer'], // Additional elements to strip
debug: true, // Enable debug logging
});| Option | Type | Default | Description |
|---|---|---|---|
elementsToStrip |
string[] |
[] |
Additional HTML elements to remove (added to defaults) |
debug |
boolean |
false |
Enable debug logging |
The following elements are always stripped:
nav,aside,footer,header- Navigation and layoutscript,style,noscript- Code and stylingsvg,iframe- Embedded content
You can keep them by adding data-llm='keep' to your elements.
| Variable | Type | Default | Description |
|---|---|---|---|
GREMLLM_BASE_URL |
string |
undefined |
Override base URL for internal fetches. Useful when running behind a proxy/load balancer with SSL termination. Example: http://localhost:3000 |
Use case: When your application is behind a load balancer or ingress with HTTPS, but runs on HTTP internally, set this to avoid SSL errors:
# In your deployment
GREMLLM_BASE_URL=http://localhost:3000This will make gremllm fetch pages using http://localhost:3000/path instead of the external https://yourdomain.com/path.
- gremllm/lib - Core Rust library for HTML conversion
- koffi - Fast and easy-to-use Node.js FFI library
Issues and PRs welcome! This is an open-source project.
Help Wanted:
- Add caching layer example
- Performance benchmarks
- Integration tests
- Streaming support for large pages
MIT