# HP1006: Attribute missing from the server HTML

> HP1006 (missing attribute): React renders an attribute on the client that the server HTML lacks, and hydration never adds it. The causes and how to fix it.

Source: https://hydration.jscrate.dev/docs/issues/hp1006
Last updated: 2026-09-18

HP1006 (`missing-attribute`) means React renders an attribute on the client
that the server HTML does not have. React does not add it during hydration, so
the element goes without it until it re-renders: a link without its `target`,
a control without its `aria-*` state. Render the attribute on the server as
well, or add it after mount.

| | |
| --- | --- |
| Code | `HP1006` |
| Name | `missing-attribute` |
| Default severity | Error |
| Group | DOM mismatches |
| What it means | React renders an attribute on the client that the server HTML does not have. |

## What HP1006 (`missing-attribute`) means

The element exists on both sides, but one attribute is only in React's client
props. The component computed it from something only the browser has, so the
server rendered `undefined` and left it out.

React does not patch attributes during hydration, and React 19 production
builds report nothing. hydration-proof compares every reused element's
attributes with the props React renders for it, so it finds the missing
attribute in production too. A missing `class` or `style` is reported as
[HP1004](https://hydration.jscrate.dev/docs/issues/hp1004) or [HP1003](https://hydration.jscrate.dev/docs/issues/hp1003).

## The React error it matches

In development, React 19 prints:

```text
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up.
```

See [attributes didn't match](https://hydration.jscrate.dev/docs/errors/tree-hydrated-but-attributes-didnt-match).

## Likely causes

- A value from a [browser-only API](https://hydration.jscrate.dev/docs/causes/browser-api), such as touch
  support or `navigator.language`, that is `undefined` on the server.
- A flag read from [browser storage](https://hydration.jscrate.dev/docs/causes/storage) or a
  [media query](https://hydration.jscrate.dev/docs/causes/media-query).
- A [random value](https://hydration.jscrate.dev/docs/causes/random) or
  [generated id](https://hydration.jscrate.dev/docs/causes/unstable-id) created only in the browser.

## How to fix it

- Render the attribute on the server as well, or add it after mount in
  `useEffect`.

```tsx title="share-button.tsx"
"use client";

export function ShareButton() {
  const touch = typeof window !== "undefined" && "ontouchstart" in window;

  return (
    <button type="button" data-input={touch ? "touch" : undefined}>
      Share
    </button>
  );
}
```

Start from the value the server can render, and switch after hydration:

```tsx title="share-button.tsx"
"use client";

import { useEffect, useState } from "react";

export function ShareButton() {
  const [touch, setTouch] = useState(false);

  useEffect(() => {
    setTouch("ontouchstart" in window);
  }, []);

  return (
    <button type="button" data-input={touch ? "touch" : undefined}>
      Share
    </button>
  );
}
```

## Prevent it with ESLint

[`no-browser-global-in-render`](https://hydration.jscrate.dev/docs/rules/no-browser-global-in-render) and
[`no-window-render-branch`](https://hydration.jscrate.dev/docs/rules/no-window-render-branch) report the
browser reads and branches that cause it.

## When the difference is intentional

`suppressHydrationWarning` on the element keeps the server's attributes without
a warning; hydration-proof then lists the difference as
[HP6001](https://hydration.jscrate.dev/docs/issues/hp6001) (info). `ignore.attributes` stops comparing an
attribute everywhere. See [ignoring findings](https://hydration.jscrate.dev/docs/ignoring).

## Example

```text
  ✖ /posts/42 1.1s  1 error
    HP1006 Attribute missing from the server HTML  (browser-only api used during render, 60%)
      article > button  in ShareButton
      attribute: data-input
      server: (absent)
      client: "touch"
      components/share-button.tsx:7:5
      → Code like `typeof window !== "undefined"` or window/navigator/document access renders differently on the server. Read browser values in useEffect, or render the component on the client only.
```

## Related

- [HP1005: attribute only in the server HTML](https://hydration.jscrate.dev/docs/issues/hp1005)
- [HP1002: attribute differs](https://hydration.jscrate.dev/docs/issues/hp1002)
- [useEffect and two-pass rendering](https://hydration.jscrate.dev/docs/guides/useeffect-two-pass-rendering)
- [Browser-only APIs during render](https://hydration.jscrate.dev/docs/causes/browser-api)
