by Karan Thakkar

How to use Babel macros with React Native

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

jfjrz1ddbDi4Zef64CpbkFH3aucozCj7VNNX
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.
m8Pk2rDXI2f6OfOEECt99XwbiR0pw5OxzXXf
Load all locales provided by Intl.js

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

LzitpUOexDJoOucbHFAbe7kkPP8nOVs2F7k5
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.

9yAnzljqibKRoDn7rKe5AkGMQ63pPWfp4354
⬆️️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!

1WKwUJ0aFROJGwvTQNckj87aOJOTvadtYTuv
yj8t50UemnN-BaJUUy0q1XAgggZgMDPgelZz
LEFT: codegen macro to build imports for all locales · RIGHT: Supported locales list
yPXysDYsOX2BytVUIzJKeY7pMK9CblD8oMeH
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:

1PuJiRDOV4A2YT6nx1SOTmeViF9XbpG1-L22
u1tVA9klTM1XTlJtK5Rj2PE5nxrzkUdtsh9F
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 ?
EVMYOZ1LD-n8SHTuqvfwVNWXr6wghjIFiND3
zJD4HRaNxhHMHsHlhlINJf64K0B0vHin32jG
  • use template literals inside your codegen ?
MVDROCDmMzE6MY8Osky7BpEJYlh-z5-QO-Ez
HXwx6D60GMfXD6DiiMmwqUI78dt4lowhLdrw

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 ?

NeELdCuvtLGOOP54bsUaMrsh3SDAd9cmI3FD

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: