Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom React components with an sx prop can’t live alongside HTML elements using the @jsxImportSource theme-ui pragma #2454

Open
kapowaz opened this issue Aug 30, 2023 · 4 comments

Comments

@kapowaz
Copy link

kapowaz commented Aug 30, 2023

If a custom React component has an API with a prop called sx, that prop is ignored if the component is called alongside any other HTML element with an sx prop augmented using the /** @jsxImportSource theme-ui */ pragma.

To Reproduce

Let’s say I have some arbitrary JSX on a page, where I’m styling arbitrary HTML elements using the sx prop, and the /** @jsxImportSource theme-ui */ pragma:

/** @jsxImportSource theme-ui */

export default () => {
  return (
    <div sx={{ color: 'blue'; }}>
      hello world
    </div>
  );
}

Let’s also say that I have a custom React component which accepts an sx prop as part of its own API, because I want to be able to pass those styles through to some part of the component’s internal implementation:

/** @jsxImportSource theme-ui */

import React from 'react';
import { ThemeUICSSObject } from 'theme-ui';

export const MyComponent = ({
  children,
  sx,
}: {
  children: React.ReactNode;
  sx: ThemeUICSSObject;
}) => {
  return (
    <div sx={sx}>
      {children}
    </div>
  );
}

If I then try to use these two components together, like this:

/** @jsxImportSource theme-ui */
import { MyComponent } from './MyComponent';

export default () => {
  return (
    <div sx={{ color: 'blue'; }}>
      <MyComponent sx={{ color: 'red'; }>Hello World</MyComponent>
    </div>
  );
}

…I would expect this to set the inner text to have a color property of red, but instead the value passed to this inner sx prop acts as if it’s undefined:

/** @jsxImportSource theme-ui */

import React from 'react';
import { ThemeUICSSObject } from 'theme-ui';

export const MyComponent = ({
  children,
  sx,
}: {
  children: React.ReactNode;
  sx: ThemeUICSSObject;
}) => {
  // in the example above, this would output '{ sx: undefined }'
  console.log({ sx });

  return (
    <div sx={sx}>
      {children}
    </div>
  );
}

Expected behavior

I would expect the style object passed to the sx prop of the custom component to correctly be passed through to the child.

Additional context

The problem only seems to be manifest when mixing these two types of usage of the sx prop, however; if in the scope of a single source file you are exclusively passing the sx prop to custom components, or exclusively passing it to plain HTML objects, they will work (and so as a workaround we will often split up the code, but this is sometimes not convenient).

@hasparus
Copy link
Member

hasparus commented Aug 30, 2023

Thank you for the issue!

What framework are you using? And what version of Theme UI do you have?

What I mean by it, this is weird and not intended. Both the tests here and all my apps using Theme UI would entirely break if this happened to me, so let's try to figure out why it happens, what kind of setup do you have.

@kapowaz
Copy link
Author

kapowaz commented Aug 30, 2023

Hi, and thanks for the prompt reply!

We’re using theme-ui 0.15.5, and the bug is exhibited both in a couple of applications that consume these components; one is a Gatsby application, and the other is a craco app.

The components we build using Theme UI are part of a design system monorepo which are published as private npm packages (containing CJS/ESM), then consumed by various internal applications (mostly some variety of create react app), as well as the two main apps I mentioned above (one is used as our internal documentation site, the other is essentially for visual regression testing). To try and streamline the experience for internal developers, we publish our own theme package which wraps most of the theme-ui dependencies, along with providing our theme defaults.

Does that answer your questions, or are there any other specifics that would be helpful?

@hasparus
Copy link
Member

hasparus commented Sep 3, 2023

If this is closed source, I'd like to see the Gatsby config and Babel and TypeScript configs.

It seems that the issue is somewhere around JSX runtime configuration, so the config files might lead us towards the cause.

@ontech7
Copy link

ontech7 commented Dec 17, 2023

I have the same problem with NextJs 14.

In fact I have to change the name to ssx in order to be ok with Theme UI style system, so like

<Comp ssx={{ mt: 30 }} />

export default function Comp({ children, ssx }: Props) {
  return <div sx={ssx}>{children}</div>
}

I'm using the tsconfig in order to avoid putting the pragma at every .tsx file

The reason is probably here. It just check if key === 'sx', and skips it. It should check if it's a React component, and not a primitive one (like h1, h2, div, etc.). I should be able to call a prop sx in a React component.

// theme-ui/packages/core/src/parseProps.tsx

export function parseProps(props: any) {
  if (!props || (!props.sx && !props.css)) return props

  const next: Record<string, unknown> = {}

  for (let key in props) {
    if (key === 'sx') continue
    next[key] = props[key]
  }

  next.css = getCSS(props)
  return next
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants