# HP1008: Element only present in the server HTML

> HP1008 (extra element): the server HTML contains a node the client render does not produce, so React discards that part of the page. Causes and the fix.

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

HP1008 (`extra-element`) means the server HTML contains an element or text node
that React's first client render does not produce. React cannot match it, so it
discards the server HTML of that part of the page and renders it again. Use the
same condition on both sides instead of rendering server-only nodes.

| | |
| --- | --- |
| Code | `HP1008` |
| Name | `extra-element` |
| Default severity | Error |
| Group | DOM mismatches |
| What it means | The server HTML contains a node the client render does not produce. |

## What HP1008 (`extra-element`) means

A component rendered something on the server and nothing (or less) in the
browser. The finding points at the parent element the node was in, and shows
the node as the server value, with `(absent)` as the client value.

Scripts are not counted, and neither are `<link>`, `<meta>`, `<style>`,
`<title>` and `<base>` tags in `<head>` ([HP1014](https://hydration.jscrate.dev/docs/issues/hp1014) covers
the head). A whitespace-only text node on one side is
[HP1015](https://hydration.jscrate.dev/docs/issues/hp1015), and an element the browser's parser moved
because of invalid nesting is [HP3001](https://hydration.jscrate.dev/docs/issues/hp3001).

## The React error it matches

```text
Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client.
Warning: Did not expect server HTML to contain a <div> in <div>
```

The first is React 19, the second the React 18 development warning. See
[Expected server HTML to contain a matching](https://hydration.jscrate.dev/docs/errors/expected-server-html-to-contain-a-matching),
which covers both React 18 structure warnings.

## Likely causes

- A component that returns `null` in the browser when a stored flag is set:
  [browser storage](https://hydration.jscrate.dev/docs/causes/storage).
- A server-only branch (`typeof window === "undefined"`) or another
  [browser-only API](https://hydration.jscrate.dev/docs/causes/browser-api) check.
- A list that is shorter on the client because the client fetched it again:
  [different data](https://hydration.jscrate.dev/docs/causes/data).
- A [media query](https://hydration.jscrate.dev/docs/causes/media-query) that hides a section in the
  browser.

## How to fix it

- Do not render server-only nodes inside hydrated components; use the same
  condition on both sides.

A cookie notice that returning visitors never see in the browser, but always
get in the server HTML:

```tsx title="cookie-notice.tsx"
"use client";

export function CookieNotice() {
  if (typeof window !== "undefined" && localStorage.getItem("cookies-ok")) {
    return null;
  }

  return <p className="notice">We use cookies.</p>;
}
```

Render what the server rendered, then hide it after hydration:

```tsx title="cookie-notice.tsx"
"use client";

import { useEffect, useState } from "react";

export function CookieNotice() {
  const [accepted, setAccepted] = useState(false);

  useEffect(() => {
    setAccepted(localStorage.getItem("cookies-ok") === "1");
  }, []);

  if (accepted) return null;
  return <p className="notice">We use cookies.</p>;
}
```

Better still, keep the choice in a cookie and read it on the server, so the
notice is never sent to someone who accepted it.

## Prevent it with ESLint

[`no-storage-in-initial-render`](https://hydration.jscrate.dev/docs/rules/no-storage-in-initial-render) and
[`no-window-render-branch`](https://hydration.jscrate.dev/docs/rules/no-window-render-branch) report the
checks that make a component render less in the browser.

## Example

```text
  ✖ / 980ms  1 error
    HP1008 Element only present in the server HTML
      #footer  in SiteFooter
      server: "<p class=\"notice\">We use cookies.</p>"
      client: (absent)
      components/site-footer.tsx:6:5
      → Do not render server-only nodes inside hydrated components; use the same condition on both sides.
```

The selector and the source line are those of the parent, here the footer that
renders `CookieNotice`. The HTML report shows the markup on both sides.

## Related

- [HP1009: element missing from the server HTML](https://hydration.jscrate.dev/docs/issues/hp1009)
- [HP1007: a different element](https://hydration.jscrate.dev/docs/issues/hp1007)
- [localStorage and sessionStorage during render](https://hydration.jscrate.dev/docs/causes/storage)
- [useEffect and two-pass rendering](https://hydration.jscrate.dev/docs/guides/useeffect-two-pass-rendering)
