feathericons/feather · error · Error

`feather.replace()` only works in a browser environment.

Error message

`feather.replace()` only works in a browser environment.

What it means

feather.replace() is a browser-only convenience API: it scans the DOM for elements with a `data-feather` attribute and replaces them with the corresponding SVG markup. It calls `document.querySelectorAll`, which only exists in a browser (or a DOM-emulating environment). The library throws this error immediately when `typeof document === 'undefined'` so you get a clear message instead of an opaque ReferenceError.

Source

Thrown at src/replace.js:13

/* eslint-env browser */
import classnames from 'classnames/dedupe';

import icons from './icons';

/**
 * Replace all HTML elements that have a `data-feather` attribute with SVG markup
 * corresponding to the element's `data-feather` attribute value.
 * @param {Object} attrs
 */
function replace(attrs = {}) {
  if (typeof document === 'undefined') {
    throw new Error('`feather.replace()` only works in a browser environment.');
  }

  const elementsToReplace = document.querySelectorAll('[data-feather]');

  Array.from(elementsToReplace).forEach(element =>
    replaceElement(element, attrs),
  );
}

/**
 * Replace a single HTML element with SVG markup
 * corresponding to the element's `data-feather` attribute value.
 * @param {HTMLElement} element
 * @param {Object} attrs
 */
function replaceElement(element, attrs = {}) {
  const elementAttrs = getAttrs(element);
  const name = elementAttrs['data-feather'];

View on GitHub (pinned to 3dc050d974)

Solutions

  1. Call feather.replace() only after the DOM is available, e.g. in a browser entry point or a useEffect/componentDidMount-style hook with a browser check.
  2. Guard the call: only invoke it when typeof document !== 'undefined'.
  3. In SSR frameworks, move the call into client-side lifecycle code or a 'use client' / mounted-only block.
  4. In Jest, set testEnvironment to 'jsdom' if you need to test replace().
  5. If rendering outside a browser, use the string-returning APIs instead: feather.icons[name].toSvg(attrs) and inject the markup yourself.

Example fix

// before
import feather from 'feather-icons';
feather.replace(); // throws in Node/SSR

// after
import feather from 'feather-icons';
if (typeof document !== 'undefined') {
  feather.replace();
}
Defensive patterns

Strategy: fallback

Validate before calling

if (typeof document === 'undefined') { /* skip replace, use toSvg strings instead */ }

Type guard

const canUseFeatherReplace = (): boolean => typeof document !== 'undefined' && typeof document.querySelectorAll === 'function';

Try / catch

try {
  feather.replace();
} catch (e) {
  if (e.message.includes('browser environment')) {
    // SSR/Node path: render icons via feather.icons[name].toSvg() instead
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling feather.replace() in Node.js (no DOM), in a server-side rendering pass (Next.js/Nuxt/Rails SSR), inside a Web Worker, or in a test runner (Jest node environment) where `document` is not defined.

Common situations: Importing feather-icons in a Node script to pre-render icons; calling replace() in a component's server-side render function; running unit tests with testEnvironment:'node'; using the package in an Electron main process.

Related errors


AI-assisted analysis of feathericons/feather@3dc050d974 (2026-08-30). Data as JSON: /api/errors/4705529eb0b651ab. Report an issue: GitHub.