Describe the bug
When a JSX element has an array css prop and spreads props (<div css={[a, b]} {...props} /> — the standard forwardRef pattern), @swc/plugin-emotion 14.8.0 and later wrap the array in a render-time css([...]) call. That css() runs without theme context, so any theme-function element ((theme) => css({ ... })) is stringified instead of resolved (Emotion logs "Functions that are interpolated in css calls will be stringified"), and its declarations are silently dropped from the output.
14.7.0 does not wrap — it leaves the array raw, so Emotion's JSX runtime resolves the theme function correctly.
The wrapping only happens when {...props} is present; an array css prop without a spread is left raw even on 14.14.0.
Input (input.tsx)
import { css } from '@emotion/react';
import { forwardRef } from 'react';
const styles = {
row: (theme) => css({ display: 'grid', gap: theme.spacing.sm }),
};
// array css prop + {...props} spread
export const Row = forwardRef((props, ref) => <div ref={ref} css={[styles.row, {}]} {...props} />);
Repro (repro.js)
const { transformSync } = require('@swc/core');
const fs = require('fs');
const out = transformSync(fs.readFileSync('input.tsx', 'utf8'), {
filename: 'input.tsx',
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic', importSource: '@emotion/react' } },
experimental: {
plugins: [[require.resolve('@swc/plugin-emotion'), { autoLabel: 'never' }]],
},
},
});
console.log(out.code);
npm i @swc/core @swc/plugin-emotion@<version>
node repro.js
Actual (@swc/plugin-emotion 14.8.0 – 14.14.0)
css: /*#__PURE__*/ (0, _react.css)([styles.row, {}]),
At runtime css([styles.row, {}]) is evaluated with no theme → styles.row is stringified → display: grid and gap never reach the DOM.
Expected (matches @swc/plugin-emotion 14.7.0)
The raw array is passed through, and Emotion's JSX runtime resolves the theme function with the active theme, so display: grid / gap are applied.
Bisect
| Version |
Output |
Theme fn resolved? |
| 14.7.0 |
raw array |
✅ yes |
| 14.8.0 → 14.14.0 |
wrapped css([...]) |
❌ no (stringified) |
Impact
Every forwardRef component that spreads props onto an element with a theme-function array css prop loses those styles. This is a common pattern, so the effect is broad (in our case it broke notebook layout — display: grid missing on a row). Workaround is pinning @swc/plugin-emotion to 14.7.0.
Version
@swc/plugin-emotion: 14.8.0 – 14.14.0 (14.7.0 OK)
@swc/core: 1.15.43 (reproduces across versions)
Describe the bug
When a JSX element has an array
cssprop and spreads props (<div css={[a, b]} {...props} />— the standardforwardRefpattern),@swc/plugin-emotion14.8.0 and later wrap the array in a render-timecss([...])call. Thatcss()runs without theme context, so any theme-function element ((theme) => css({ ... })) is stringified instead of resolved (Emotion logs "Functions that are interpolated in css calls will be stringified"), and its declarations are silently dropped from the output.14.7.0does not wrap — it leaves the array raw, so Emotion's JSX runtime resolves the theme function correctly.The wrapping only happens when
{...props}is present; an arraycssprop without a spread is left raw even on 14.14.0.Input (
input.tsx)Repro (
repro.js)Actual (
@swc/plugin-emotion14.8.0 – 14.14.0)At runtime
css([styles.row, {}])is evaluated with no theme →styles.rowis stringified →display: gridandgapnever reach the DOM.Expected (matches
@swc/plugin-emotion14.7.0)The raw array is passed through, and Emotion's JSX runtime resolves the theme function with the active theme, so
display: grid/gapare applied.Bisect
css([...])Impact
Every
forwardRefcomponent that spreads props onto an element with a theme-function arraycssprop loses those styles. This is a common pattern, so the effect is broad (in our case it broke notebook layout —display: gridmissing on a row). Workaround is pinning@swc/plugin-emotionto14.7.0.Version
@swc/plugin-emotion: 14.8.0 – 14.14.0 (14.7.0 OK)@swc/core: 1.15.43 (reproduces across versions)