innerRef
How to access the underlying ref
Sometimes you need access to the
ref
of the underlying component that's rendered. You can accomplish this with
the innerRef
prop.
This is a function and if provided, will be called with the inner element's reference.
Often you'll bring glamorous
into an existing project which is already
using global CSS. Many of the glamorous
APIs make working with this
as easy as possible.
glamorous
works out of the box with CSS modules too. It Just Worksβ’.
Here's an example using Bootstrap:
// source https://v4-alpha.getbootstrap.com/components/alerts/
import React from 'react';
import { render } from 'react-dom';
import glamorous from 'glamorous';
const Alert = glamorous.div('alert', props => `alert-${props.type}`);
function App() {
return (
<glamorous.Div maxWidth={600} margin="70px auto" fontSize={24}>
<Alert type="success">
<strong>Success!</strong> Tada! π
</Alert>
<Alert type="info">
<strong>Heads up!</strong> Some info here βΉοΈ
</Alert>
<Alert type="warning">
<strong>Warning!</strong> Something's up β οΈ
</Alert>
<Alert type="danger">
<strong>Oh snap!</strong> This is not good π¨
</Alert>
</glamorous.Div>
);
}
render(<App />, document.getElementById('root'));
Try this out in your browser here!
If
// source https://v4-alpha.getbootstrap.com/components/alerts/
import React from 'react';
import { render } from 'react-dom';
import glamorous from 'glamorous';
const Alert = glamorous.div('alert', props => {
const types = ['success', 'info', 'warning', 'danger'];
return types.reduce((all, t) => {
if (props.hasOwnProperty(t)) {
all = `${all} alert-${t}`;
}
return all;
}, '');
});
function App() {
return (
<glamorous.Div maxWidth={600} margin="70px auto" fontSize={24}>
<Alert success>
<strong>Success!</strong> Tada! π
</Alert>
<Alert info>
<strong>Heads up!</strong> Some info here βΉοΈ
</Alert>
<Alert warning>
<strong>Warning!</strong> Something's up β οΈ
</Alert>
<Alert danger>
<strong>Oh snap!</strong> This is not good π¨
</Alert>
</glamorous.Div>
);
}
render(<App />, document.getElementById('root'));
Try this out in your browser here!
Remember this
With CSS in JS, the goal is to style components and reuse those
components. With this in mind, if you need to style your entire application
(like html
/body
or add some reset
styles), you wont do this with
glamorous
. Instead you can use regular CSS or use glamor's API for
injecting global styles.
In addition, rather than using CSS to style an a
tag with global CSS,
you should create a Link
component with all the styles you need and
reuse that.
glamorous
fully supports theming using a special <ThemeProvider>
component.
It provides the theme
to all glamorous components down the tree.
import glamorous, {ThemeProvider} from 'glamorous'
// our main theme object
const theme = {
main: {color: 'red'}
}
// our secondary theme object
const secondaryTheme = {
main: {color: 'blue'}
}
// a themed <Title> component
const Title = glamorous.h1({
fontSize: '10px'
}, (props, theme) => ({
color: theme.main.color
}))
// use <ThemeProvider> to pass theme down the tree
<ThemeProvider theme={theme}>
<Title>Hello!</Title>
</ThemeProvider>
// it is possible to nest themes
// inner themes will be merged with outers
<ThemeProvider theme={theme}>
<div>
<Title>Hello!</Title>
<ThemeProvider theme={secondaryTheme}>
{/* this will be blue */}
<Title>Hello from here!</Title>
</ThemeProvider>
</div>
</ThemeProvider>
// to override a theme, just pass a theme prop to a glamorous component
// the component will ignore any surrounding theme, applying the one passed directly via props
<ThemeProvider theme={theme}>
{/* this will be yellow */}
<Title theme={{main: {color: 'yellow'}}}>Hello!</Title>
</ThemeProvider>
Try this out in your browser here!
glamorous
also exports a withTheme
higher order component (HOC) so you can access your theme in any component!
import glamorous, {ThemeProvider, withTheme} from 'glamorous'
// our main theme object
const theme = {
main: {color: 'red'}
}
// a themed <Title> component
const Title = glamorous.h1({
fontSize: '10px'
}, (props, theme) => ({
color: theme.main.color
}))
// normal component that takes a theme prop
const SubTitle = ({children, theme: {color}}) => (
<h3 style={{color}}>{children}</h3>
)
// extended component with theme prop
const ThemedSubTitle = withTheme(SubTitle)
<ThemeProvider theme={theme}>
<Title>Hello!</Title>
<ThemedSubTitle>from withTheme!</ThemedSubTitle>
</ThemeProvider>
Try this out in your browser here!
Or if you prefer decorator syntax:
import React, {Component} from 'react'
import glamorous, {ThemeProvider, withTheme} from 'glamorous'
// our main theme object
const theme = {
main: {color: 'red'}
}
// a themed <Title> component
const Title = glamorous.h1({
fontSize: '10px'
}, (props, theme) => ({
color: theme.main.color
}))
// extended component with theme prop
@withTheme
class SubTitle extends Component {
render() {
const {children, theme: {main: {color}}} = this.props
return <h3 style={{color}}>{children}</h3>
}
}
<ThemeProvider theme={theme}>
<Title>Hello!</Title>
<SubTitle>from withTheme!</SubTitle>
</ThemeProvider>
withTheme
expects aThemeProvider
further up the render tree and will warn indevelopment
if one is not found!
context is an unstable API and it's not recommended to use it directly. However, if you need to use it for some reason, here's an example of how you could do that:
const dynamicStyles = (props, context) => ({
color: context.isLoggedIn ? 'green' : 'red'
})
const MyDiv = glamorous.div(dynamicStyles)
MyDiv.contextTypes = {
isLoggedIn: PropTypes.string,
}
class Parent extends React.Component {
getChildContext() {
return {
isLoggedIn: true,
}
}
render() {
return <MyDiv />
}
}
Parent.childContextTypes = {
isLoggedIn: PropTypes.string,
}
<Parent />
// renders <div />
// with {color: 'green'}
If your use case is really size constrained, then you might consider using the "tiny" version of glamorous for your application.
It is a miniature version of glamorous
with a few limitations:
glamorous.article({/* styles */ })
)
So you have to create your own (glamorous('article')({/* styles */ })
)glamorous.Span
)glam
prop (see the example below).ThemeProvider
or withTheme
, you must import those manually.
They are not exported as part of glamorous/ tiny
like they are with glamorous
.Here's an example of what you're able to do with it.
import React from 'react'
import glamorous from 'glamorous/dist/glamorous.es.tiny'
const Comp = glamorous('div')({
color: 'red'
}, (props) => ({
fontSize: props.glam.big ? 20 : 12
}))
function Root() {
return (
<Comp
glam={{ big: true }}
thisWillBeForwardedAndReactWillWarn
>
ciao
</Comp>
)
}
export default Root
Improved Experience
It's recommended to use either
babel-plugin-module-resolver
or the resolve.alias
config with webpack so you don't have
to import from that full path.
You have the following options available for this import:
glamorous/dist/glamorous.es.tiny.js
- use if you're using Webpack@>=2 or Rollupglamorous/dist/glamorous.cjs.tiny.js
- use if you're not transpiling ESModulesglamorous/dist/glamorous.umd.tiny.js
- use if you're including it as a script tag. (There's also a .min.js
version).Important note
Because both glamor
and react
support SSR, glamorous does too! I actually do this
on my personal site which is generated at build-time
on the server. Learn about rendering
react
on the server and
glamor
too.
To perform server-side rendering, use renderStatic
from glamor
, which takes a callback. Render your component inside the callback and all of the calls to css()
will be collected and generated html and css will be returned. This will also return an array of ids to rehydrate the styles for fast startup.
To perform rehydration, call rehydrate
with the array of ids returned by renderStatic
.
Example -
import {renderStatic} from 'glamor/server'
import {rehydrate} from 'glamor'
import {render} from 'react-dom'
import ReactDOMServer from 'react-dom/server'
let {html, css, ids} = renderStatic(() => ReactDOMServer.renderToString(<App />))
return `
<html>
<head>
<style>${css}</style>
</head>
<body>
<div id="app">${html}</div>
<script src="./bundle.js"></script>
<script>
rehydrate(${JSON.stringify(ids)});
render(<App />, document.getElementById('app'));
</script>
</body>
</html>
`
Check out this example on how to use glamorous with Next.js
Glamorous might add duplicate styles to the page due to the intricate nature of es6 imports.
For example:
import App from './App';
rehydrate(${JSON.stringify(ids)}) // ids returned by renderStatic
As import statements get transpiled before other statements, so rehydrate
will be called after regardless of what order you list the statements. This will result in duplicate styles because App
is imported first, so all the styles are added before rehydrate
runs.
To tackle this problem, use a require()
call to order the statements.
rehydrate(${JSON.stringify(ids)});
const App = require('./App');
Or rehydrate
must be run before any other style code if you use any other solution.