Gathering detailed insights and metrics for vanilla-extract-react-bake
Gathering detailed insights and metrics for vanilla-extract-react-bake
Gathering detailed insights and metrics for vanilla-extract-react-bake
Gathering detailed insights and metrics for vanilla-extract-react-bake
A variant-focused typesafe component factory for vanilla extract recipes, obviously
npm install vanilla-extract-react-bake
Typescript
Module System
Node Version
NPM Version
TypeScript (96.76%)
JavaScript (3.24%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
10 Stars
30 Commits
2 Watchers
1 Branches
1 Contributors
Updated on Apr 15, 2024
Latest Version
0.5.0
Package Id
vanilla-extract-react-bake@0.5.0
Unpacked Size
39.14 kB
Size
8.75 kB
File Count
9
NPM Version
10.0.0
Node Version
20.6.0
Published on
Sep 29, 2023
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
24
4
Heavily inspired by styled
from stitches
, this package provides a bake
function that can be used to generate typed components that reflect a vanilla-extract recipe, where variants are props.
1npm install --save vanilla-extract-react-bake
Create a recipe using @vanilla-extract/recipes
.
1import { recipe } from '@vanilla-extract/recipes'; 2 3export const myRecipe = recipe({ 4 base: { 5 color: 'gray' 6 }, 7 8 variants: { 9 size: { 10 small: { 11 fontSize: 12 12 }, 13 medium: { 14 fontSize: 16 15 }, 16 large: { 17 fontSize: 20 18 } 19 } 20 } 21});
Create a component using bake
with your recipe.
1import { bake } from 'vanilla-extract-react-bake'; 2import { myRecipe } from './myRecipe.css'; 3 4export const MyComponent = bake('div', myRecipe);
Use that component with props that match the variants in your recipe.
1import { MyComponent } from './MyComponent'; 2 3export const MyApp = () => { 4 return ( 5 <MyComponent size="large">Hello world</MyComponent> 6 ); 7};
Since we're already piped to create components with bake
, we can also use it to create simple components that just have a classname.
1const MyComponent1 = bake('div'); 2const MyComponent2 = bake('div', 'some-classname'); 3const MyComponent3 = bake('button', [ 4 'my', 5 'list', 6 'of', 7 'classnames' 8]);
Since it works with regular classname, that means we can also use it for regular vanilla extract imports.
1import { myStyles } from './myStyles.css'; 2 3const MyComponent = bake('div', myStyles);
I'd love for there to be a way to infer whether a variant is required or not, but that doesn't seem possible with the current types in vanilla-extract
. So instead, you can mark a variant as required in the config, which is the optional third argument.
1const MyComponent = bake('div', basic, { 2 required: ['requiredVariantName'] 3});
By default, we pull out the variants from the props before we spread them onto the resulting component. In the vast majority of cases, this is what you'd want, because there is no html attribute that has the same name as your variant, and you'd make react mad at you.
However, in some cases, you may want to name one of your variants after an html attribute. This tends to happen with disabled
or required
or something like that on form elements. For these cases you can use the forward
config option:
1const MyComponent = bake('div', basic, { 2 forward: ['disabled'] 3});
This will both trigger the disabled
variant, and pass the disabled
prop through to the resulting component.
as
Runtime base component overrideIn some cases you want to override the underlying that was extended at runtime. It's not suggested to make large shifts in the base component type, but usually just provide a more specific version of the same thing. Though it also pays off in some cases where you're abstracting over something like a link
and button
behind the scenes.
Honestly I don't know how strong the types are when you do this. I'm not smart enough yet to understand how that would work.
You can pass a string
1const MyComponent = bake('div', basic); 2 3export const MyApp = () => { 4 return <MyComponent as="span">Hello world</MyComponent>; 5};
Or you can pass another component
1import { Link as NextLink } from 'next/link'; 2import { Link } from './myBakedLink'; 3 4export const MyApp = () => { 5 return ( 6 <Link as={NextLink} href="/some-page"> 7 Some page 8 </Link> 9 ); 10};
The resulting components from bake
can be used with other classnames, and the resulting classnames will be merged together.
1const MyComponent = bake('div', 'some-classname'); 2 3export const MyApp = () => { 4 return <MyComponent className="another-classname" />; 5};
The resulting html will append them together in the order of the bake
classname, and then the inlined classname.
inject
This feature allows you to inject a prop builder for a custom prop on the resulting component. This is useful for entirely different reasons than most of the rest of the code, but it's nearly free to add to this specific code, so I thought I'd give it a shot.
It's useful when you want your component to have some sort of custom prop, and the value passed to that prop results in values being set on other non-custom props.
1const MyComponent = bake('div', basic, { 2 inject: { 3 // This adds a 'css' prop that just passes the 4 // value through to the style attribute 5 css: (val: React.CSSProperties) => { 6 return { style: val }; 7 } 8 } 9}); 10 11export const MyApp = () => { 12 // the `css` prop here is typed as React.CSSProperties 13 // from the declaration above in the inject config 14 return <MyComponent css={{ color: '#BADA55' }} />; 15};
This particular example of just 'renaming' the style
attribute to css
isn't interesting, but you could imagine that css
takes in the values from your css-in-js library of choice, and then outputs the actual style values to the style
attribute, or emits a class to className.
NOTE: there's a lot of nuance in the order that injected props are executed and merged together. They also don't have any other introspection into the component values at the moment (e.g. you can't read the value of children and use it to build your result).
makeBake
I think inject
is quite powerful when you can use it across your entire design system. However, it's a bit of a bummer to need to pass the same inject
config to every single bake
call. So I've added a makeBake
function that allows you to create a bake
function that has the inject
config already applied.
1import { makeBake } from 'vanilla-extract-react-bake';
2
3const myBake = makeBake({
4 inject: {
5 // This adds a 'css' prop that just passes the
6 // value through to the style attribute
7 css: (val: React.CSSProperties) => {
8 return { style: val };
9 }
10 }
11});
12
13// myBake is now a function that can be used to create components
14// just like `bake`, but the `css` injection we did above will
15// always be applied to all resulting components
16const MyComponent = myBake('div', basic);
17
18export const MyApp = () => {
19 // the `css` prop here is typed as React.CSSProperties
20 // from the declaration above in the inject config
21 return <MyComponent css={{ color: '#BADA55' }} />;
22};
The code for actually building the components is relatively straightforward, and is only a few lines of code. The primary benefit here is the type safety that comes with it. Much like with vanilla-extract
directly, a large amount of your code is built out at compile time. So you ship less javascript to the client, you have types that never drift from your styles, and you have to write less code by hand.
It's primarily good for folks who spend a lot of time reimplementing their style variants as javascript props on their components, a common job for people making design systems, or just reusable components in general. bake
allows you to have a single place where your variants are defined, and then outputs a very well-typed component that can be used with those variants, and updates when the variants change.
It's especially apparent when you look at the intellisense for components that come out of bake
.
No vulnerabilities found.
No security vulnerabilities found.