How to use Babel macros with React Native

A practical use case for solving an i18n problem using codegen.macro

Karan Thakkar
Jun 8, 2018 · 5 min read
Image for post
Image for post
Background Photo by Rayi Christian Wicaksono on Unsplash

If you follow Kent C. Dodds or Sunil Pai on Twitter, you may have read tweets every once in a while about babel macros. So did I. But it was only yesterday that I finally understood what the hype is all about. And it is glorious!

So, coming to the problem: I wanted to add some utility to do locale-based number formatting in React Native. Since there is no consistent support for the Internationalization API in React Native, I used a polyfill for it: https://github.com/andyearnshaw/Intl.js. Now, along with the polyfill, I also needed to import all the supporting locale files. There are two options here:

  1. Load all the locales: This is straightforward, as I can just import one file. This should usually be avoided, since it can unnecessarily bloat your bundle size if you just need to support some locales.
Image for post
Image for post
Load all locales provided by Intl.js

2. Load only necessary locales: With this, I only load the locales that my app supports.

Image for post
Image for post
Load only the necessary locales from Intl.js

For example, if the app supports 40 locales, I have to manually end up writing 40 imports for each locale. It becomes much harder and more tedious to do this as the list of locales you support increases.

I wanted to automate this in a way that required no manual changes. This is especially useful for us as we have background jobs running on the CI that automatically update our locales file whenever we add support for a new language.

How can I dynamically import multiple files but also allow the React Native packager to have all the file paths at compile time? babel-plugin-macros and codegen.macro 💜

What are these…babel things?

This blog post by Kent C. Dodds perfectly describes what babel-plugin-macros is:

It’s a “new” approach to code transformation. It enables you to have zero-config, importable code transformations.

codegen.macro is one such transformation that you can use to “generate code” at build time.

How do you set it up?

React Native allows you to configure your own babel settings. You can create our own “.babelrc” file at the root of your project. To make sure that you use the default babel configuration that comes with React Native, install babel-preset-react-native.

On top of this you have to install another module: codegen.macro. The codegen plugin uses babel-plugin-macros under the hood to do its job. We’ll see in a bit what that is.

Image for post
Image for post
⬆️️This is how your .babelrc file should look

What does codegen.macro do?

It takes a piece of code, executes it, and replaces itself with the export-ed string. It will make a lot of sense once you see the example below. Given a list of locales and a codegen macro, it generates a list of imports at build time!

Image for post
Image for post
Image for post
Image for post
LEFT: codegen macro to build imports for all locales · RIGHT: Supported locales list
Image for post
Image for post
Output from babel after transpilation

But, what if I need syntax highlighting?

Since we’re writing all our code in a template string, it is really hard to get proper syntax highlighting. You might end up spending a lot of time trying to figure out why your macro gives an error while transpiling.

Thankfully, babel macros support a few different ways to use them. My favorite is using a codegen.require. With this, you can move the body of your macro into a separate file and require it wherever you want, as shown below:

Image for post
Image for post
Image for post
Image for post
Import the codegen using a special codegen.require call

Advantages of using this syntax:

  • well, syntax highlighting 💁🏻‍
  • no need to escape any escape sequences you need to use like \n 🙄
Image for post
Image for post
Image for post
Image for post
  • use template literals inside your codegen 👯
Image for post
Image for post
Image for post
Image for post

NOTE: upgrading React Native

If you choose to override the babel config, whenever you upgrade react-native, you must also bump the version of babel-preset-react-native to match the one being used inside that react-native version.

That’s it folks! You’ve setup babel macros with React Native 🎈🎉 Check out these other available macros if you wanna try out something different.

PS: Thanks to Narendra N Shetty, Siddharth Kshetrapal and Kent C. Dodds for reviewing the draft and helping shape it better 🤗

Image for post
Image for post

Hi! 👋‍ I’m Karan Thakkar. I work on React Native infrastructure at Skyscanner Engineering. Previously, I lead the web team at Crowdfire. I like trying out new technologies in my spare time and I’ve built Tweetify (using React Native) and Show My PR’s (using Golang).

Other articles written by me are:

freeCodeCamp.org

This is no longer updated.

Thanks to Narendra N Shetty

Karan Thakkar

Written by

he/him 💖 staff engineer @Transferwise ⚛️ prev: @Skyscanner, @Crowdfire 👨‍💻

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Karan Thakkar

Written by

he/him 💖 staff engineer @Transferwise ⚛️ prev: @Skyscanner, @Crowdfire 👨‍💻

freeCodeCamp.org

This is no longer updated. Go to https://freecodecamp.org/news instead

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

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