Migrate from styled-components
To move a codebase from styled-components to motif, start with the import surface, port one component family at a time, and let the two libraries co-exist until the cut-over is complete. Most patterns map directly; a handful do not.
What maps directly
| styled-components | motif |
|---|---|
styled.div\color: red;`` | styled('div', { base: { color: 'red' } }) |
| `styled(Component)``... | styled(Component, { base: ... }) |
${(props) => props.theme.colors.text} | '$colors.text.default' |
<Component as="a"> | <Component as="a"> (identical) |
${(props) => props.size === 'lg' && css\...`` | variants: { size: { lg: { ... } } } |
<ThemeProvider theme={theme}> | <ThemeProvider themes={[theme]} active="light"> |
${(props) => props.theme.media.md\...`` | <Box p={{ base: '$space.2', md: '$space.4' }} /> |
&:hover { ... } | _hover={{ ... }} (Box / Pressable) |
keyframes\...`` | animation token preset, or a CSS string in transition |
The shape is one-to-one for most of what a styled-components app does day-to-day. The biggest mental shift is from template literals to typed objects.
Mismatches and gaps
A few things do not have a direct equivalent. They each have a workaround.
- The
csshelper. styled-components'cssreturns a fragment composable into a styled template. Motif's equivalent is just a plainStylePropsobject you spread intobaseor a variant. There is no fragment language. attrs. styled-components'attrslets a styled component pre-set HTML attributes. In motif, pass attributes through props —styleddoes not consume them, so<MyButton type="submit">reaches the rendered element.- Theme function callbacks. styled-components supports
props => props.theme.…callbacks inside template literals. Motif uses'$colors.…'references — the resolver does the same job at runtime, but at no point does a function call enter the render path. - Global styles. styled-components ships
createGlobalStyle. Motif does not. Use a plain<style>tag in your document head, or mount one through your SSR framework's head manager. - Server rendering. styled-components'
ServerStyleSheetand motif'sSSRStyleCollectorsolve the same problem with different APIs. See Server-side rendering for the motif side.
Migrate a component
A typical styled-components button:
Becomes:
Two things to notice. The conditional template literal becomes a typed variants map — the
intent prop is now type-checked against the keys of that map. The &:hover block becomes a
_hover object — same selector under the hood, hashed and shared across the page.
Run them side by side
styled-components and motif both inject CSS into the document; their style buckets do not collide. You can install both at the same time, port one component family per pull request, and remove styled-components when nothing imports it any more. There is no big-bang day.
The migration order that has worked for most codebases:
- Install motif and define the theme. Put a
<ThemeProvider>next to (or replacing) the existingThemeProviderfrom styled-components. - Move primitives first.
Box,Stack,Text, the basic typography. These have the most call sites and the highest leverage. - Move components in dependency order. Anything that depends on a primitive moves after the primitive does.
- Delete styled-components last. When
grep "from 'styled-components'"returns nothing, uninstall the package and clean up the ThemeProvider.