# How to Disable headroom.js on Mobile Devices: A Complete Guide

> Learn how to disable headroom.js on mobile devices by conditionally applying headroom middleware. This guide provides a straightforward solution for optimizing your site's performance on smaller screens.

- Repository: [Tejas Chopra/headroom](https://github.com/chopratejas/headroom)
- Tags: how-to-guide
- Published: 2026-06-11

---

**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`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/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.

```typescript
// 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:

```typescript
// 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`](https://github.com/chopratejas/headroom/blob/main/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:

```typescript
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`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/client.ts) already sends an `X-Headroom-Mode` header, which you can extend for mobile detection:

1. **Add a header check**: Modify your middleware to inspect `request.headers["x-headroom-mode"]` or a custom `x-device-type` header.
2. **Early abort**: If the header indicates mobile, skip the compression logic and return the raw handler.
3. **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:

```css
@media (max-width: 768px) {
  .headroom {
    position: static;
  }
}

```

While this keeps [`headroom.js`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/sdk/typescript/src/adapters/vercel-ai.ts) | Exposes `headroomMiddleware` that injects [`headroom.js`](https://github.com/chopratejas/headroom/blob/main/headroom.js) into the request flow. |
| [`sdk/typescript/src/client.ts`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/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 `conditionalHeadroomMiddleware` to detect mobile user agents in `navigator.userAgent` and return a no-op function instead of `headroomMiddleware`.
- **Preserve type safety**: The pattern requires no changes to `chopratejas/headroom` internals and maintains full TypeScript compatibility.
- **Server-side alternatives**: Use headers like `X-Headroom-Mode` for 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`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/headroom/proxy.py) and the transform modules in `headroom/transforms/*` operate independently of the client-side middleware. Disabling [`headroom.js`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/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`](https://github.com/chopratejas/headroom/blob/main/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.