Setting up twin.macro with Vite + React
Tags: react, tailwindcss, javascript, vite
Published: 2024-06-13 14:34:16

## Introduction

Recently, I encountered a project which worked with twin macro. My first thought was that Tailwind is already perfect, so why want more? Then I started to use twin.macro, and I have gotten addicted to it. Not only does it have massive flexibility in styling, but you can also use it with emotion, or styled-components, and it is very clean too, when combined with it’s vs code IntelliSense extension.

So, my goal was to go from having my tailwind looking like this

```
const SpanStyled = ({ active, children }) => (
  <span className={`px-9 py-2 font-bienvenido text-base cursor-pointer rounded-full 
    ${active ? 'bg-brown-600 text-white' : 'bg-transparent text-black'} 
    hover:bg-brown-200`}>
    {children}
  </span>
);

```
to this

```
const SpanStyled = styled.span(({ active }: { active: boolean }) => [
  tw`px-9 py-2 font-bienvenido text-base cursor-pointer rounded-full hocus:(bg-brown-20)`,
  active ? tw`bg-brown-600 text-white-900` : tw`bg-transparent text-black-900`
])

```

I do not need to tell you which would be easier to maintain.


## The Setup

### Starting with tailwind and dependency installs

Aight, let's jump into it. This is a step-by-step process, which would help you use and be able to set up twin.macro for your vite + react or any other project you bootstrap with vite (I think).

So you have to do the first things first, bootstrap your project as so;

```
npm create vite@latest
```
I used the typescript variant. So, you follow the prompt.
Then you install tailwind and its cohorts.

```
npm install @emotion/styled @emotion/css
```

```
npm install -D tailwindcss autoprefixer postcss twin.macro babel-plugin-macros vite-plugin-babel-macros @emotion/babel-plugin @emotion/babel-plugin-jsx-pragmatic @types/babel-plugin-macros @babel/plugin-transform-react-jsx

```
Aight, when you have installed all these, you would then need to initialise your tailwind with the postcss and autoprefixer, so you do;

```
npx tailwindcss init -p
```

Don't forget to add these

```
@tailwind base;
@tailwind components;
@tailwind utilities;
```

to your `tailwindconfig.json`, inside the content object,

```
 "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
```

### Folder and file configs for usage

Okay, we are all done with that. Now to the entire configurations.
Now, if you follow [Ben Rogerson’s docs](https://github.com/ben-rogerson/twin.examples/tree/master/vite-emotion-typescript) in his explanation, you will see the ability to add the babel field to your `package.json`, or to create a babel-plugin-macros.config.js file and add some code to it. I would suggest you rename it to `.mjs`, instead of `.js`
If you are adding to the package.json, you add;

```
"babelMacros": {
  "twin": {
    "preset": "emotion"
  }
},
```

Then, if you are creating the babel config, you add this;

```
module.exports = {
  twin: {
    preset: 'emotion',
  },
}
```

Alright, well done for coming this distance. All that remains for you to update is your vite.config, add your twin.d.ts, modify your tsconfig.json, and create and import your global styles. It is super easy.
For your `vite.config.ts`, or `js` for whichever, replace with this;

```
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import macrosPlugin from 'vite-plugin-babel-macros'

// https://vitejs.dev/config/
export default defineConfig({
  optimizeDeps: {
    esbuildOptions: {
      target: 'es2020',
    },
  },
  esbuild: {
    jsxFactory: 'jsx',
    jsxInject: 'import { jsx } from "@emotion/react"',
    logOverride: { 'this-is-undefined-in-esm': 'silent' },
  },
  plugins: [
    react({
      babel: {
        plugins: [
          'babel-plugin-macros',
          [
            '@emotion/babel-plugin-jsx-pragmatic',
            {
              export: 'jsx',
              import: '__cssprop',
              module: '@emotion/react',
            },
          ],
          [
            '@babel/plugin-transform-react-jsx',
            { pragma: '__cssprop' },
            'twin.macro',
          ],
        ],
      },
    }),
    macrosPlugin(),
  ],
  define: {
    'process.env': {},
  },
})

```

When you are done with that, you then create a `types` folder at the root of your project and create a `twin.d.ts` or `js`, dependent, and add this

```
import 'twin.macro'
import { css as cssImport } from '@emotion/react'
import styledImport from '@emotion/styled'
import { CSSInterpolation } from '@emotion/serialize'

declare module 'twin.macro' {
  // The styled and css imports
  const styled: typeof styledImport
  const css: typeof cssImport
}

declare module 'react' {
  // The tw and css prop
  interface DOMAttributes<T> {
    tw?: string
    css?: CSSInterpolation
  }
}
```

In my case, I also created a `.babelrc.js`, and then added this to it `_this is not necessary!_`

```
module.exports = {
  presets: [
    [
      "next/babel",
      {
        "preset-react": {
          runtime: "automatic",
          importSource: "@emotion/react",
        },
      },
    ],
  ],
  plugins: ["@emotion/babel-plugin", "babel-plugin-macros"],
};
```


The only places we need to modify in our tsconfig.json, are;

```
"skipLibCheck": true,
"jsxImportSource": "@emotion/react",
```

and

```

"include": ["src", "types"],
```

Here, you would just add “types”, to the already existing src that is there, and you are good to go.
Then, you create a styles folder inside your src folder and create a `GlobalStyles.tsx` inside it. Then you would place this code inside it

```
import React from 'react'
import { Global } from '@emotion/react'
import tw, { css, theme, GlobalStyles as BaseStyles } from 'twin.macro'

const customStyles = css({
  body: {
    WebkitTapHighlightColor: theme`colors.purple.500`,
    ...tw`antialiased`,
  },
})

const GlobalStyles = () => (
  <>
    <BaseStyles />
    <Global styles={customStyles} />
  </>
)

export default GlobalStyles
```

after you would then import it to your main.tsx, so it affects your application globally. Then, you are good to use twin.macro to start styling your components.

## Conclusion.

So, when you are done setting it up, you should be able to edit your code optimally, and use the `tw`, instead of the `className`. Also note you have to import `tw` at the head of each document as you start to work on it.

So, from this

![App.tsx before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e45reu0echml7xq8qz0x.png)

![main.tsx before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8uh4gokdtwgtaaw6swnl.png)

we have this...


![App.tsx after](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dz4rrlx9nrcb3vyyuhm.png)


![main.tsx after](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67bnab61zav0mcumalz4.png)

If you want access to the file itself, and see how it was done, this is the repository here

```
https://github.com/Franklivania/vite-twin.gitv
```

Cheers and happy coding 🍻&💖