#1020·svgr

SVGR strips px from CSS variables in inline styles

Author: HondaYtCreated Feb 4, 2026Updated Feb 4, 2026

Bug Report

When converting inline styles, SVGR strips the px unit from CSS custom property values (e.g., --s: 20px becomes --s: 20). React does not auto-append px to custom properties, so rendering breaks.

To Reproduce

  1. Run (version pinned for reproducibility):
bash
npx @svgr/[email protected] --no-svgo <<< '<svg style="--s: 20px;" viewBox="0 0 20 20"><path d="M0 0"/></svg>'
  1. Observe output:
javascript
import * as React from "react";
const SvgComponent = (props) => (
  <svg style={{ "--s": 20 }} viewBox="0 0 20 20" {...props}><path d="M0 0" /></svg>
);
export default SvgComponent;

Expected output

javascript
import * as React from "react";
const SvgComponent = (props) => (
  <svg style={{ "--s": "20px" }} viewBox="0 0 20 20" {...props}><path d="M0 0" /></svg>
);
export default SvgComponent;

Why This Happens

In packages/hast-util-to-babel-ast/src/stringToObjectStyle.ts, the formatValue function converts numeric values (and px-suffixed values) to numbers for all properties:

typescript
const formatValue = (value: string) => {
  if (isNumeric(value)) return t.numericLiteral(Number(value))
  if (isConvertiblePixelValue(value))
    return t.numericLiteral(Number(trimEnd(value, 'px')))  // ← strips px even for CSS custom properties
  return t.stringLiteral(value)
}

This optimization is safe for standard CSS properties because React auto-appends px to numeric values (e.g., width: 20width: 20px). However, React treats CSS custom property values as raw strings (no unit inference), so the unit is lost.

The fix in #582 (cbdb47f) correctly handled key formatting for --* properties, but its test case used --index: 1 (a unitless number), so this edge case wasn't caught.

Suggested Fix

This makes custom property values always stay as strings. That changes --index: 1 from a number to "1", but that is consistent with how CSS custom properties are interpreted (string values passed through as-is).

Pass the key to formatValue and preserve string values for CSS custom properties:

typescript
const formatValue = (value: string, key: string) => {
  // CSS custom properties: always preserve original string value
  if (VAR_REGEX.test(key)) return t.stringLiteral(value)

  if (isNumeric(value)) return t.numericLiteral(Number(value))
  if (isConvertiblePixelValue(value))
    return t.numericLiteral(Number(trimEnd(value, 'px')))
  return t.stringLiteral(value)
}

Update the call site in stringToObjectStyle:

typescript
const property = t.objectProperty(formatKey(key), formatValue(value, key))

Suggested Test Cases

typescript
// packages/hast-util-to-babel-ast/src/index.test.ts

it('preserves px unit in CSS custom property values', () => {
  expect(stringToObjectStyle('--size: 20px')).toMatchInlineSnapshot(`
    {
      "--size": "20px",
    }
  `)
})

it('preserves other units in CSS custom property values', () => {
  expect(stringToObjectStyle('--spacing: 1.5rem; --width: 100%')).toMatchInlineSnapshot(`
    {
      "--spacing": "1.5rem",
      "--width": "100%",
    }
  `)
})

it('preserves complex CSS custom property values', () => {
  expect(stringToObjectStyle('--gradient: linear-gradient(red, blue)')).toMatchInlineSnapshot(`
    {
      "--gradient": "linear-gradient(red, blue)",
    }
  `)
})

Note: In the PR, I covered the regression at the transform (integration) level rather than adding direct stringToObjectStyle unit tests. This still verifies that custom property values (including units) are preserved in the generated output.

Workaround

Note: This quick workaround only handles integer px values (e.g., 20px). It does not cover decimals, negatives, or other units.

Wrap CSS custom property values in calc() to bypass the /^\\d+px$/ regex:

javascript
// svg-css-var-fix-loader.js (use before @svgr/webpack)
module.exports = function(source) {
  return source.replace(
    /style="([^"]*)"/g,
    (match, styleContent) => {
      const fixed = styleContent.replace(
        /(--[\\w-]+:\\s*)(\\d+px)/g,
        '$1calc($2)'
      )
      return `style="${fixed}"`
    }
  )
}

Related

  • #582 — Inline CSS variable support
  • cbdb47f — fix: support CSS variables

Link to repl or repo (highly encouraged)

Not available. CLI reproduction included above.

Run npx envinfo --system --binaries --npmPackages @svgr/core,@svgr/cli,@svgr/webpack,@svgr/rollup --markdown --clipboard

Paste the results here:

bash
System:
 - OS: Linux 6.12 Amazon Linux 2023
 - CPU: (11) arm64 unknown
 - Memory: 4.15 GB / 7.65 GB
 - Container: Yes
 - Shell: 5.2.15 - /bin/bash
Binaries:
 - Node: 24.13.0 - /home/node/.nvm/versions/node/v24.13.0/bin/node
 - npm: 11.7.0 - /home/node/.nvm/versions/node/v24.13.0/bin/npm
npmPackages:
 - @svgr/core: not installed
 - @svgr/cli: not installed
 - @svgr/webpack: ^8.1.0 => 8.1.0
 - @svgr/rollup: not installed