Introducing glamorous for React Native 💄

A complimentary component library from glamorous, inspired by styled-components 💅 and jsxstyle for styling ⚛ React Native Components

The glamorous-native logo, shamelessly inspired (with permission) by styled-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.
- Gajus Kuizinas

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.

// glamorous
const 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:

// glamorous
const 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 React
function 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 React
class 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.

// glamorous
const glamorousAnimatedFactory = glamorous(Animated.View)
const MyAnimatedView = glamorousAnimatedFactory(styles.baseStyle)
// allows us to add styles rules as properties
MyAnimatedView.propsAreStyleOverrides = true
class 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:

// glamorous
import 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} = glamorous
function 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