I've noticed while writing components that the sx prop didn't work on custom components which i'd built myself, but when i went to use it with other plugin libraries (fontawesome for example) it did work as expected. So am i missing something? I can't imagine large libraries like FA are doing something special to accept the sx prop.
An example component i've made (though this seems to affect everything i've written, so i'm curious what's wrong).
/** @jsx jsx */
import { jsx } from 'theme-ui'
interface PropTypes {
to: string
variant?: string
children: string
}
const Button = ({ to, variant, children }: PropTypes) => (
<a
href={to}
className={`button`}
sx={{
px: 3,
py: 2,
border: 0,
borderRadius: 4,
cursor: 'pointer',
variant: variant ? `buttons.${variant}` : `buttons.primary`,
}}
>
{children}
</a>
)
export default Button
<Button to='#' variantbutton='primary'>Button text</Button>
What i'd like to do, as an example
```
It's not as automagical as you think. It's pretty special but not automagical persay.
You're not passing the sx prop to the Button component...
You can have two sx props though! so on your component add another right afterwards sx={sx} and add sx to your incoming props {to,variant,children,sx}
What @robmoggach
The sx prop must be passed down all the way to the actual HTML element..
Otherwise, it'll get lost & there's no way for Emotion to know what to style!
EDIT: I was wrong! Please see jxnblk's comment below :))
You鈥檒l need to pass the className prop through to the underlying HTML element. The sx prop is converted to className with Theme UI鈥檚 jsx function
@jxnblk
All this time... I'd been passing ...props down thinking it was sx doing the magic.
Woops! 馃槄
So i didn't mention that i'd passed {...props} down before and that didn't work (and i'd already tried sx={sx} for good measure but i had no idea it'd already been converted to a className).
Fails
const Button = ({ to, variant, children }: PropTypes, props: any) => (
<a
{...props}
...
</a>
)
Works, but with TS errors
const Button = ({ to, className, variant, children }: PropTypes) => (
<a
className={className}
...
</a>
)
So, this wasn't working because of how i was destructuring (not sure if once an arg has been destructured in a function it can be called again?) but if i put in className as a destructured argument it works ... but TS is erroring because i'm never defining className on the parent.
Solution
const Button = (props: PropTypes) => {
const { to, variant, children } = props
return (
<a
{...props}
...
</a>
)
}
This works and throws no errors but i will be getting some redundant props. My button will actually end up looking like this, which isn't great and could definitely require some cleaning up but i'm not sure how without causing more errors/ts warnings.
const Button = (props: PropTypes) => {
const { to, variant, children } = props
return (
<a
href={to}
className={`button asd-123-Button`} //merging properly, yay!
sx={{...}}
to={to} //redundant & obfuscated
variant={variant} //redundant & obfuscated
children={children} //redundant & obfuscated
>
{children}
</a>
)
}
Okay i think i've got a solution which is clean & gives no errors.
Best solution so far
/* imports */
interface PropTypes {
to: string,
variant: string,
sx: object, //we expect a sx prop but never use it below
className: string, //we expect a className prop but never define one when the component is called
children: string
}
const Button = (props: PropTypes) => { //props is defined and compared against the interface
const { to, variant, className, children } = props //then we destructure later, ignore sx because we don't use it
return (
<a
href={to}
className={className} //merging properly, yay!
sx={{...}}
>
{children}
</a>
)
}
<Button to='#' variant='button.primary' sx={{ display: 'flex' }}>Button text</Button>
No duplicated props, and everything is used some way or another.
Going to close as i'm happy with this solution (especially as sx errors on html elements anyway in ts so removing the errors in my own components is 馃憣)
Thanks all!
Most helpful comment
You鈥檒l need to pass the
classNameprop through to the underlying HTML element. Thesxprop is converted toclassNamewith Theme UI鈥檚jsxfunction