How to Disable headroom.js on Mobile Devices: A Complete Guide
You can disable headroom.js on mobile devices by wrapping the SDK's headroomMiddleware in a conditional check that detects mobile user agents and returns a no-op middleware instead of the compression logic.
The headroom-ai SDK from chopratejas/headroom injects headroom.js into web pages to compress LLM-bound context before each request. While this compression improves performance on desktop browsers, you may want to disable it on mobile devices to reduce JavaScript overhead or avoid compatibility issues.
Why Disable headroom.js on Mobile?
Mobile devices often operate with constrained CPU resources and limited battery life. The headroomMiddleware—which runs on every request through the client-side script—adds processing overhead that may outweigh the benefits of payload compression on smaller screens. Additionally, mobile browsers may handle the DOM manipulation differently, making conditional disabling a practical optimization strategy.
Method 1: Conditional Middleware with User-Agent Detection
The most reliable approach intercepts the middleware initialization before the SDK injects the script. Because the middleware executes in sdk/typescript/src/adapters/vercel-ai.ts, you can wrap the headroomMiddleware function with a guard clause that checks the device type.
Detecting Mobile Browsers
The SDK does not ship with a built-in mobile detection flag, but you can implement a standard user-agent check using navigator.userAgent. On the server side, you should default to assuming non-mobile to prevent hydration mismatches.
// Detection logic for client-side execution
const isMobile = typeof navigator !== "undefined"
? /Mobi|Android|iPhone|iPad|iPod/.test(navigator.userAgent)
: false; // Server-side render → assume non-mobile
This regex covers the majority of mobile and tablet devices. For enterprise applications requiring more granular detection, substitute this with a dedicated library like mobile-detect or the navigator.userAgentData API.
Implementing the Conditional Wrapper
Create a factory function that returns either the real headroomMiddleware or an identity function based on the device detection:
// src/adapters/vercel-ai.ts (excerpt)
import { headroomMiddleware } from "headroom-ai/vercel-ai";
/**
* Returns a Vercel AI SDK middleware stack that automatically disables
* headroom.js on mobile browsers.
*/
export function conditionalHeadroomMiddleware(
options: CompressOptions = {}
) {
// 1️⃣ Detect mobile browsers.
const isMobile = typeof navigator !== "undefined"
? /Mobi|Android|iPhone|iPad|iPod/.test(navigator.userAgent)
: false;
// 2️⃣ If we're on a mobile device, return a no-op middleware.
if (isMobile) {
console.info("[headroom] Skipping compression on mobile device");
return (handler: any) => handler; // Identity – does nothing
}
// 3️⃣ Otherwise, delegate to the real headroom middleware.
return headroomMiddleware(options);
}
When isMobile evaluates to true, the function returns (handler) => handler, which passes requests through unchanged. This effectively prevents headroom.js from loading or executing without modifying the core SDK source code.
Integration with Vercel AI SDK
Replace the standard headroomMiddleware import with your conditional wrapper in your AI provider configuration:
import { conditionalHeadroomMiddleware } from "./adapters/vercel-ai";
import { openaiProvider, wrapLanguageModel } from "headroom-ai/vercel-ai";
export const openai = openaiProvider({
apiKey: process.env.OPENAI_API_KEY,
});
export const model = openai.chat.completions.create({
model: "gpt-4o-mini",
});
export const llm = wrapLanguageModel(model, {
// Replace headroomMiddleware with the conditional wrapper
middleware: conditionalHeadroomMiddleware(),
});
This pattern maintains type safety and requires no changes to the Python proxy (headroom/proxy.py) or the transform modules in headroom/transforms/*.
Method 2: Server-Side Environment Flags
If you prefer centralized control, expose a query-string parameter or header that the middleware reads server-side. The HeadroomClient in sdk/typescript/src/client.ts already sends an X-Headroom-Mode header, which you can extend for mobile detection:
- Add a header check: Modify your middleware to inspect
request.headers["x-headroom-mode"]or a customx-device-typeheader. - Early abort: If the header indicates mobile, skip the compression logic and return the raw handler.
- Propagation: Set this header in your edge middleware (Next.js, Vercel Edge, etc.) based on the user agent.
This approach keeps mobile detection logic on the server, reducing client-side JavaScript execution entirely.
Method 3: CSS-Only Toggle
For scenarios where you want the script loaded but visually disabled, combine the middleware with CSS media queries:
@media (max-width: 768px) {
.headroom {
position: static;
}
}
While this keeps headroom.js running in the background, it prevents the UI shifts associated with the header compression. Use this method only when you need the compression logic active but want to disable visual effects on mobile.
Key Source Files and Architecture
Understanding the repository structure helps when implementing these conditional checks:
| File | Role |
|---|---|
sdk/typescript/src/adapters/vercel-ai.ts |
Exposes headroomMiddleware that injects headroom.js into the request flow. |
sdk/typescript/src/client.ts |
Sends the X-Headroom-Mode header that the proxy reads; useful for custom mobile detection headers. |
headroom/transforms/* (Python) |
Implements the actual compression algorithms; unaffected by client-side toggles. |
sdk/typescript/test/adapters/vercel-ai.test.ts |
Reference for how the middleware is normally applied; useful for testing your conditional wrapper. |
The core compression pipeline remains operational regardless of client-side configuration changes, as the Python proxy handles the actual payload transformation.
Summary
- Wrap the middleware: Use
conditionalHeadroomMiddlewareto detect mobile user agents innavigator.userAgentand return a no-op function instead ofheadroomMiddleware. - Preserve type safety: The pattern requires no changes to
chopratejas/headroominternals and maintains full TypeScript compatibility. - Server-side alternatives: Use headers like
X-Headroom-Modefor edge-based mobile detection if you prefer server-side logic. - CSS fallback: Media queries can disable visual effects without stopping the script execution, though this is less optimal for performance.
Frequently Asked Questions
How does headroom.js detect mobile devices by default?
The headroom-ai SDK does not include built-in mobile detection. According to the source code in sdk/typescript/src/adapters/vercel-ai.ts, the headroomMiddleware runs on every request regardless of device type. You must implement the detection logic yourself using navigator.userAgent checks or server-side headers.
Will disabling headroom.js on mobile affect the Python compression pipeline?
No. The Python proxy at headroom/proxy.py and the transform modules in headroom/transforms/* operate independently of the client-side middleware. Disabling headroom.js on mobile only prevents the client-side script injection; if compression is configured server-side, it will still process requests from mobile devices unless you explicitly disable it there as well.
Can I use feature detection instead of user-agent sniffing?
Yes, though user-agent detection is the most straightforward method shown in the vercel-ai.ts adapter. You can modify the conditionalHeadroomMiddleware to check for specific Canvas API support, touch events, or screen dimensions using window.matchMedia. However, user-agent regex remains the most reliable way to detect mobile browsers before the middleware initializes.
Where should I place the mobile detection logic in a Next.js application?
Place the conditionalHeadroomMiddleware function in a utility file (e.g., lib/headroom.ts) and import it into your API route handlers or server components. Avoid importing it directly into client components that execute navigator checks during server-side rendering, as this causes hydration errors. Use the typeof navigator !== "undefined" guard shown in the source code to handle SSR safely.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →