glamorous
APIThe glamorous
function allows you to create your own glamorousComponentFactory
(see
below) for any component you have.
const MyComponent = props => <div {...props} />
const myGlamorousComponentFactory = glamorous(MyComponent)
const MyGlamorousComponent = myGlamorousComponentFactory({/* styles */})
<MyGlamorousComponent id="i-am-forwarded-to-the-div" />
Try this out in your browser here!
You can also provide a few options to help glamorous know how to handle your component:
The displayName
of a React component is used by React in the
React DevTools
and is really handy for debugging
React applications. Glamorous will do its best to give a good displayName
for your component,
but, for the example above, the best it can do is: glamorous(MyComponent)
. If you want to specify
a displayName
, you can do so with this property.
const MyComponent = props => <div {...props} />
const myGlamorousComponentFactory = glamorous(
MyComponent,
{displayName: 'MyGlamorousComponent'}
)
Try this out in your browser here!
Note: the
displayName
can also included in the className that your components are given which makes the development experience a bit nicer. To enable this see the section aboutconfig
. This will likely be enabled by default in the next major change.
And now all components created by the myGlamorousComponentFactory
will have the displayName
of
MyGlamorousComponent
.
There is also a babel plugin that can
monkey-patch the displayName
onto the components that you create from your component factory.
React has an Unknown Prop Warning that it
logs when you pass spurious props to DOM elements: (i.e. <div big={true} />
). Because you can style your
components using props, glamorous needs to filter out the props you pass so it doesn't forward these on to
the underlying DOM element. However, if you create your own factory using a custom component, glamorous will
just forward all the props (because the component may actually need them and glamorous has no way of knowing).
But in some cases, the component may be spreading all of the props onto the root element that it renders.
For these cases, you can tell glamorous which element is being rendered and glamorous will apply the same logic
for which props to forward that it does for the built-in factories.
const MyComponent = props => <div {...props} />
const myGlamorousComponentFactory = glamorous(
MyComponent,
{rootEl: 'div'}
)
const MyGlamorousComponent = myGlamorousComponentFactory(props => ({
fontSize: props.big ? 36 : 24,
}))
<MyGlamorousComponent big={true} id="room423" />
// this will render:
// <div id="room423" />
// with {fontSize: 36}
// `big` is not forwarded to MyComponent because the `rootEl` is a `div` and `big`
// is not a valid attribute for a `div` however `id` will be forwarded because
// `id` is a valid prop
Try this out in your browser here!
There are some cases where you're making a glamorousComponentFactory
out of a custom component that spreads
some of the properties across an underlying DOM element, but not all of them. In this case you should use
rootEl
to forward only the right props to be spread on the DOM element, but you can also use forwardProps
to specify extra props that should be forwarded.
const MyComponent = ({shouldRender, ...rest}) => (
shouldRender ? <div {...rest} /> : null
)
const MyStyledComponent = glamorous(MyComponent, {
forwardProps: ['shouldRender'],
rootEl: 'div',
})(props => ({
fontSize: props.big ? 36 : 24,
}))
<MyStyledComponent shouldRender={true} big={false} id="hello" />
// this will render:
// <div id="hello" />
// with {fontSize: 24}
// `shouldRender` will be forwarded to `MyComponent` because it is included in
// `forwardProps`. `big` will not be forwarded to `MyComponent` because `rootEl`
// is a `div` and that's not a valid prop for a `div`, but it will be used in
// the styles object function that determines the `fontSize`. Finally `id` will
// be forwarded to `MyComponent` because it is a valid prop for a `div`.
Try this out in your browser here!
Most of the time, glamor is super fast, but in some scenarios it may be nice to
prevent glamor from computing your styles when you know the class name should
not change. In these cases, you can implement shouldClassNameUpdate
. For
example:
const pureDivFactory = glamorous('div', {
shouldClassNameUpdate(props, previousProps, context, previousContext) {
// return `true` to update the classname and
// `false` to skip updating the class name
return true
},
})
const Div = pureDivFactory({marginLeft: 1})
render(<Div css={{marginLeft: 2}} />)
// this will render:
// <div />
// with {marginLeft: 2}
Note that this is not the same as shouldComponentUpdate
. Your component will
still be rerendered. shouldClassNameUpdate
is only for allowing you to opt-out
of generating the className
unnecessarily.
This allows you to use props as CSS. You always have the css
prop, but
sometimes it's really nice to use just the props as CSS.
const MyDiv = glamorous('div', {propsAreCssOverrides: true})({
margin: 1,
fontSize: 1,
})
render(<MyDiv margin={2} css={{':hover': {fontWeight: 'bold'}}} />)
// renders <div /> with margin: 2, fontSize: 1, and fontWeight: bold on hover
You can also compose the built-in components:
glamorous(glamorous.Div)(/* styles */)
In some cases you might want to just copy the styles of an already created glamorous component with a
different tag altogether, withComponent
function comes in handy then.
const Button = glamorous.button({
display: 'inline-block',
color: 'red',
fontSize: '16px',
margin: '16px',
padding: '8px 16px',
border: '1px solid red',
borderRadius: '4px',
});
// We're replacing the <button> tag with an <a> tag, but reuse all the same styles
const Link = Button.withComponent('a')
<Button>Normal Button</Button>
<Link>Normal Link</Link>
// this will render:
// <button>Normal Button</button>
// <a>Normal Link</a>
// both with the same styles
Note: to override styles, you can do the same thing you do with a regular component (
css
prop, wrap it inglamorous()
, or regularclassName
prop).
Sometimes it can be useful to apply props by default for a component. The
simplest way to do this is by simply setting the defaultProps
value on the
glamorousComponent. But if you want a little more power and composition, then
the withProps
APIs can help.
These APIs are highly composable, it would be hard to show you all the examples of how this composes together. Just know that it behaves as you might expect.
// when creating a glamorousComponentFactory
const bigDivFactory = glamorous('div', {withProps: {big: true}})
const BigDiv = bigDivFactory(({big}) => ({fontSize: big ? 20 : 10}))
render(<BigDiv />) // renders with fontSize: 20
render(<BigDiv big={false} />) // renders with fontSize: 10
// applying props to an existing component
const MyDiv = glamorous.div(({small}) => ({fontSize: small ? 10 : 20}))
const SmallDiv = MyDiv.withProps({small: true})
render(<SmallDiv />) // renders with fontSize: 20
Based on those examples, there are three places you can apply props to a glamorous component. How these props are composed together applies in this order (where later has more precedence):
Creating a glamorousComponentFactory
Directly on a glamorousComponent
with the .withProps
function
When rendering a component (just like applying props to a regular components)
In addition to this, you can also have dynamic props. And these props don't have to be used for glamorous styling, any valid props will be forwarded to the element:
const BoldDiv = glamorous
.div(({bold}) => ({fontWeight: bold ? 'bold' : 'normal'}))
.withProps(({bold}) => ({className: bold ? 'bold-element' : 'normal-element'}))
render(<BoldDiv />) // renders <div class="bold-element" /> with fontWeight: bold
render(<BoldDiv bold={false} />) // renders <div class="normal-element" /> with fontWeight: normal
The .withProps
API can also accept any number of arguments. They are called
with (accumulatedProps, context)
. accumulatedProps
refers to the props that
are known so far in the accumulation of the props which makes this API highly
composable. Finally, the withProps
APIs can also accept arrays of
objects/functions. You can pretty much do anything you want with this API.
NOTE: This is a shallow merge (uses
Object.assign
)
glamorousComponentFactory
Whether you create one yourself or use one of the built-in ones mentioned above, each glamorousComponentFactory
allows you to invoke it with styles and it returns you a new component which will have those styles applied when it's rendered. This is accomplished by generating a className
for the styles you give and forwarding that className
onto the rendered element. So if you're wrapping a component you intend to style, you'll need to make sure you accept the className
as a prop and apply it to where you want the styles applied in your custom component (normally the root element).
const UnstyledComp = ({ className, children }) => <div className={`\${className} other-class`}>{children}</div>
const MyStyledComp = glamorous(UnstyledComp)({ margin: 1 })
<MyStyledComp>content</MyStyledComp>
// rendered output: <div class="<glamor-generated-class> other-class">content</div>
// styles applied: {margin: 1}
The glamorousComponentFactory
accepts any number of style object arguments. These can be style objects or functions which are invoked with props
on every render and return style objects. To learn more about what these style objects can look like, please take a look at the glamor
documentation.
const MyStyledDiv = glamorous.div(
{
margin: 1,
},
(props) => ({
padding: props.noPadding ? 0 : 4,
})
)
<MyStyledDiv /> // styles applied: { margin: 1px; padding: 4px; }
<MyStyledDiv noPadding /> // styles applied: { margin: 1px; padding: 0; }
Tip
Tip: glamorous simply takes these style objects and forwards them to glamor
.
glamor
will then merge those together in a way you would expect. One neat
thing you can do is specify an array of style objects and glamor
will treat
that exactly the same. It's really expressive!
You can also specify other classes you'd like applied to the component as well. If these classes are generated by glamor, then their styles will be merged with the glamor style's, otherwise the class name will simply be forwarded.
const className1 = glamor.css({paddingTop: 1, paddingRight: 1}).toString()
const styles2 = {paddingRight: 2, paddingBottom: 2}
const className3 = glamor.css({paddingBottom: 3, paddingLeft: 3}).toString()
const styles4 = {paddingLeft: 4}
const styles5 = props => (props.active ? 'active' : 'not-active')
const MyStyledDiv = glamorous.div(
className1,
styles2,
className3,
styles4,
styles5,
'extra-thing',
)
<MyStyledDiv />
/*
styles applied: {
padding-top: 1px;
padding-right: 2px;
padding-bottom: 3px;
padding-left: 4px;
}
as well as 'not-active' and anything coming from `extra-thing`.
*/
Using the glamorous
TypeScript definitions
The current bundled typescript definitions are incomplete and based around the needs of the developers who contributed them.
Pull requests to improve them are welcome and appreciated. If you've never contributed to open source before, then you may find this free video course helpful.
The typings for
shouldClassNameUpdate
// Creating your own
glamorous(Component)(/* styleArgument */)
glamorous('div')(/* styleArgument */)
// Using built-in
glamorous.div<Props>(/* styleArgument */)
// Using shouldClassNameUpdate
glamorous(Component, {
shouldClassNameUpdate: (props, prevProps, context, prevContext) => props !== prevProps
})(/* styleArgument */)
// Using shouldClassNameUpdate with Context
glamorous<Props, Context>(Component, {
shouldClassNameUpdate: (props, prevProps, context, prevContext) => context !== prevContext
})(/* styleArgument */)
// Using withProps
glamorous(Component, {
withProps: {primaryColor: 'red'}
})((props) => ({/* props = { primaryColor: string } */})
const WithPropsComponent = glamorous(Component)(/* styleArgument */).withProps(withProps: {primaryColor: 'red'})
...
<WithPropsComponent primaryColor='' /> // primaryColor is an optional prop of string type based on the above
By providing the typings for Props and Theme to Glamorous when setting up your component factory they will be typed on the props argument for function arguments automatically.
interface Props {
noPadding?: boolean,
theme: { color: string }
}
const MyStyledDiv = glamorous.div<Props>(
{
margin: 1,
},
({noPadding, theme}) => ({
padding: noPadding ? 0 : 4,
color: theme.color,
})
)
<MyStyledDiv /> // styles applied: {margin: 1, padding: 4}
<ThemeProvider theme={{color: 'red'}}>
<MyStyledDiv noPadding /> // styles applied: {margin: 1, padding: 0, color: red}
</ThemeProvider>
All of these work, however you only get typesafety and intellisense on simple css key props (see the css typings).
In the future this may become possible with Microsoft/TypeScript#6579
Alternatively support for full typesafety would be possible using patterns along the lines of http://typestyle.io/.
Currently support is limited to Div
and Svg
.
Possible support via glamors typings
When using glamorous in a library that you are generating definition files for you will need to include the following import and export to get around a typescript issue Microsoft/TypeScript/issues/5938.
import glamorous, { ExtraGlamorousProps, WithComponent } from 'glamorous'
export { ExtraGlamorousProps, WithComponent }