Introducing glamorous for React Native đź’„
A complimentary component library from glamorous, inspired by styled-components đź’… and jsxstyle for styling âš› React Native Components
A few months ago Kent C. Dodds released glamorous to the React community. Inspired by styled-components, it addressed the common challenges that come with reusable styled components.
How it works
You’ve done the song and dance before:
function MyComponent ({style, ...rest}) {
return (
<View
style={[styles.baseStyle, style]}
{...rest}
/>
)
}const styles = StyleSheet.create({
baseStyle: {
backgroundColor: 'blue'
}
})
If a base style is all you’re looking for in a reusable component, the glamorous approach allows you to do the same thing in a simple and functional way:
function withStyle (WrapppedComponent, baseStyle) {
return function Component ({style, ...rest}) {
return (
<WrappedComponent style={[baseStyle, style]} {...rest} />
)
}
}
With withStyle
, MyComponent
looks more like:
const MyComponent = withStyle(View, styles.baseStyle)<MyComponent style={{padding: 10}} />// renders:
// <View style={{backgroundColor: 'blue', padding: 10}} />
You’re able to get the same thing with much less work. But there’s more that can be done following this idea:
- Dynamic styles
- Theming
- Deeper component composition
- Smarter property forwarding
Fortunately glamorous is built with this in mind, and it’s finally ready for React Native.
glamorous-nativeđź’„
glamorous-native
is a direct port of glamorous
for use with React Native. The only difference is platform components (Leland Richardson’s “primitive components”). React web’s div
becomes React Native’s View
.
A quick primer on glamorous
Let’s start with a basic example of a user interface consisting mainly of View
and Text
components:
function MyUserInterface({style}) {
return (
<View style={[styles.container, style]}>
<View style={styles.top}>
<Text style={styles.text}>Something to see up here</Text>
</View>
<View style={styles.bottom}>
<Text style={styles.text}>Something to see down here</Text>
</View>
</View>
)
}const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
alignItems: 'center',
},
top: {
padding: 40,
backgroundColor: 'lightgray',
},
bottom: {
paddingVertical: 20,
paddingHorizontal: 40,
backgroundColor: 'darkgray',
},
text: {
color: 'white'
}
}
This works, but can quickly become overwhelming as you add more to this component. You may find yourself navigating through a user interface component by the hints in the style names. The best way to understand what’s happening is to look at the styles.
With glamorous-native
we couple the styles to the components, with a meaningful name. In the process we avoid adding more noise in the file as if we were to traditionally define these components:
// Create a <Wrapper> component that renders a flexed <View> with
// it's contents centered
const Wrapper = glamorous.view({
flex: 1,
justifyContent: 'center'
alignItems: 'center',
})// Creates a <Top> component that renders a padded <View> with
// a lightgray background
const Top = glamorous.view({
padding: 40,
backgroundColor: 'lightgray',
})// Creates a <Bottom> component that renders a less padded <View>
// with a darkgray background
const Bottom = glamorous.view({
paddingVertical: 20,
paddingHorizontal: 40,
backgroundColor: 'darkgray',
})// Creates a white <Text> component
const Text = glamorous.text({
color: 'white'
})function MyUserInterface({style}) {
return (
<Wrapper style={style}>
<Top>
<Text>Something to see up here</Text>
</Top>
<Bottom>
<Text>Something to see down here</Text>
</Bottom>
</Wrapper>
)
}
This has a nice side effect, since JSX is easier to reason through. Should we make a shift towards avoiding style
properties all together? Not so fast, but you can see a path to that approach.
It removes the mapping between components and styles. Components are made into a low-level styling construct.
The important pieces are the component factory functions, .view()
and .text()
, which created our glamorous
component with the styles we provided. A style object or a StyleSheet
rule can be provided (don’t worry — style object still create StyleSheet
entries).
There’s more we can do.
Dynamic styles
If you pass a function, it will be called with the component’s properties and should return a style that depends on the value of a property.
// glamorousconst SizedText = glamorous.text(
props => ({fontSize: props.big ? 32 : 24})
)<SizedText big>
// renders: <Text>
// with a style {fontSize: 32}<SizedText>
// renders: <Text>
// with a style {fontSize: 24}
Factory functions are variadic, so you can pass in any number of styles of different types:
// glamorousconst styles = StyleSheet.create({
baseText: {margin: 10}
})const SizedText = glamorous.text(
styles.baseText,
{padding: 10},
props => ({fontSize: props.big ? 32 : 24})
)<SizedText big>
// renders: <Text>
// with a style {margin: 10, padding: 10, fontSize: 32}
This brings a different approach than how we’re used to writing a React component:
// traditional Reactfunction SizedText ({big, style, ...rest}) {
return (
<Text
style={[styles.baseText, style, {fontSize: big ? 32 : 24}]}
{...rest}
/>
)
}
Animated Styles
Animated components generally come with visual bloat, as they require inline styling.
Let’s take a square that fades out:
// traditional Reactclass FadingSquare extends React.Component {
state = {opacity: new Animated.Value(1)} ... render() {
return (
<Animated.Value
style={[
styles.baseStyle,
this.props.style,
{opacity: this.state.opacity}
]}
/>
)
}
}const styles = StyleSheet.create({
baseStyle: {
width: 100,
height: 100,
backgroundColor: 'red'
}
})
We need to account for the base style, a style that may be provided to the component, and the animated style property.
Let’s step through this using the full glamorous
API. First, glamorous
itself is a function that creates a component factory for the component you pass in. When calling the factory function with the desired styles, it creates that styled component; that’s what glamorous.view(..)
does.
// glamorousconst glamorousAnimatedFactory = glamorous(Animated.View)
const MyAnimatedView = glamorousAnimatedFactory(styles.baseStyle)
// allows us to add styles rules as properties
MyAnimatedView.propsAreStyleOverrides = trueclass FadingSquare extends React.Component {
state = {opacity: new Animated.Value(1)} ... render() {
return (
<MyAnimatedView
style={this.props.style}
opacity={this.state.opacity}
/>
)
}
}
- We’re able to compose the
baseStyle
onto the component - We’re able to add a provided style with a clean
style
property assignment - We’re able to declare the animated
style
as a property cleanly
Theming
This comes with the ThemeProvider
found in glamorous
to allow a set of styles to be accessed by any glamorous component in the render tree. A theme is accessible as the second argument in a dynamic style function:
// glamorousimport glamorous, {ThemeProvider} from 'glamorous-native'const Title = glamorous.text({
fontSize: 16
}, (props, theme) => ({
color: theme.primaryColor
}))// glamorous components within <ThemeProvider> are provided a theme
<ThemeProvider theme={theme}
<Title>Greetings</Title>
</ThemeProvider>// themes can also be nested
<ThemeProvider theme={lightTheme}>
<View>
<Title>Light greeting text</Title>
<ThemeProvider theme={darkTheme}>
<Title>Dark greeting text</Title>
</ThemeProvider>
</View>
</ThemeProvider>
You can run a hands on example here
As in glamorous
, glamorous-native
comes with a jsxstyle inspired set of components. If there’s a scenario you want avoid creating new glamorous components, and you’d rather not create a StyleSheet
entry to reference, there’s another path forward:
const {View, Text} = glamorousfunction App() {
return (
<View
flex={1}
alignItems="center"
>
<Text
fontSize={16}
color="blue"
>
That was easy!
</Text>
</View>
)
}
How to get started
You’ll find additional resources and documentation at robinpowered/glamorous-native.
If you’d like to get your hands on glamorous-native
today with some examples, you can head on over to these sample projects.
Cheers, and I hope you enjoy đź’„ing your UIs!
Made by the folks at Robin, who are most definitely hiring