The Startup
Published in

The Startup

Twin-Macro on CRA with React 17

This recipe is based on this excellent article, but with adjustments to make it work with React 17.

  1. First let’s create a CRA React app:
npx create-react-app tw-test

2. Next install all the Tailwind, twin.macro, and emotion libraries:

yarn add tailwindcss twin.macro @emotion/core @emotion/styled @emotion/react

3. Next up, we initialize Tailwind.

npx tailwindcss init --full

This creates a tailwind.config.js file in the root directory, which we point to in the package.json .

4. Add a new babelMacros key to package.json

  "babelMacros": {
"twin": {
"config": "tailwind.config.js"
}
},

You can put the tailwind configuration anywhere you like in the tree, just make sure to specify the path correctly in the package.json .

5. Import the tailwind CSS into your index.js file

import 'tailwindcss/dist/base.min.css';

This completes the setup.

Watch It On YouTube

Another way you can learn about this is to watch the video for this article.

Twin.Macro & React 17 YouTube video

Using Tailwind

There are several ways to use twin.macro , the easiest way is to use the tw attribute on any React element, like so:

/** @jsxImportSource @emotion/react */
import tw from "twin.macro";
function App() {
return (
<div tw="max-w-4xl mx-auto p-5 mt-5">
<h1 tw="text-blue-500 text-4xl">Hello world</h1>
</div>
);
}
export default App;

The @jsxImportSource syntax is new for React 17. In React 16 the syntax was @jsx , if you use that syntax you will get an error message that reads:

pragma and pragmaFrag cannot be set when runtime is automatic.

Other Ways To Use Twin-Macro

Other options for twin.macro include creating new React elements in an emotion-styled style, like so:

/** @jsxImportSource @emotion/react */
import tw from "twin.macro";
const Heading = tw.h1`text-blue-500 text-2xl p-2`;
const Container = tw.div`max-w-4xl mx-auto p-5 mt-5`;
function App() {
return (
<Container>
<Heading>My custom heading</Heading>
</Container>
);
}
export default App;

You can also extend these components:

/** @jsxImportSource @emotion/react */
import tw from "twin.macro";
const Heading = tw.h1`text-blue-500 text-2xl p-2`;
const BigHeading = tw(Heading)`text-4xl`;
const Container = tw.div`max-w-4xl mx-auto p-5 mt-5`;
function App() {
return (
<Container>
<BigHeading>My big heading</BigHeading>
</Container>
);
}
export default App;

And you can even do conditional styling:

/** @jsxImportSource @emotion/react */
import tw, { styled } from "twin.macro";
const Heading = tw.h1`text-blue-500 text-2xl p-2`;
const MaybeBigHeading = styled(Heading)(({ big }) => [
big && tw`text-4xl`
]);
const Container = tw.div`max-w-4xl mx-auto p-5 mt-5`;
function App() {
return (
<Container>
<MaybeBigHeading>My custom heading</MaybeBigHeading>
<MaybeBigHeading big>My big heading</MaybeBigHeading>{" "}
</Container>
);
}
export default App;

Next Steps

  • Use Lighthouse to check out the performance stats on your application when you build it in production mode. I think you will be impressed.
  • Use yarn build to create a production deployable version and look at the static asset sizes.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Jack Herrington

Jack Herrington

523 Followers

I YouTube as the Blue Collar Coder on advanced FE topics. Check it out!