(This guide applies to Version 2.x.x of Sanity Studio. At the time of this writing, Sanity Studio V3 is in developer preview and this guide will likely not apply to V3.)
Sanity is a headless CMS with an interesting and quite unique value proposition: it gives you, the developer, an impressive amount of control over just about everything, including the UI that content editors use to manage their structured content.
Sanity Studio is the tooling that Sanity provides for developers to customize the CMS UI experience they provide for their content authors. Out of the box, Sanity Studio provides a default experience for managing content, and allows this experience to be tweaked merely by adding/hijacking various React components. Out of the box, the Studio looks something like this:
The default Sanity Studio setup is delightful and intuitive for content editors used to working in a CMS setup. With a sprinkle of extra code, a developer can add their own unique UI pieces, such as this address autocomplete that we’ve created that connects to Google Places API that provides an autocomplete and stores address information (such as the address and geo-coordinates). This component wraps Sanity UI – a set of official Sanity UI components – so that the input component fits the design scheme of the rest of the CMS studio, but allows us to add our own functionality.
The problem: certain packages will “break” Sanity Studio V2
Building our own custom UI components with React is a treat. It allows us to use our React expertise to customize and extend the default Sanity Studio experience and even pull in additional third-party libraries, such as Zod for data validation. However, if you try to use Zod in a custom Sanity Studio component, you’ll likely run into an error that looks something like this:
ERROR in ../node_modules/zod/lib/index.mjs Module parse failed: Unexpected token (361:8) You may need an appropriate loader to handle this file type. | const fullPath = [...path, ...(issueData.path || [])]; | const fullIssue = { | ...issueData, | path: fullPath, | };
The Sanity Studio Webpack server is tripping over our Zod import, balking that zod/lib/index.mjs
has some mysterious syntax (referring to the ...
spread syntax).
If you’ve tried to incorporate any library that ships modern language features into your Sanity Studio setup, you’ve perhaps encountered a very similar issue. At first this is a bit disheartening, since it seems as though Sanity Studio's standard build configuration doesn’t “support” these libraries.
Fret not, there is a way out!
The solution: a sprinkle of Webpack configuration
One of the beauties of Sanity is that most of its components are open source; check out the Sanity organization on GitHub to see all of the wonderful work the Sanity team has made publicly available. This allows community members to poke around the codebase a bit, and come up with their own solutions based on the code itself like this example of a user determining how to customize the Sanity webpack configuration.
To get around our "modern language features" issue, we can tweak the Sanity Webpack configuration so that it uses babel to transpile the library files for our third-party libraries that are causing us problems. We’ll add a webpack.sanity.js
file to the root of our Sanity Studio project directory. We’ll start with a simple pass-through so we can inspect the Webpack configuration when we get access to it:
module.exports = function(config) { console.log(config.module.rules); // 👈 let's check out the rules return config; };
This should show us something like the following:
[ { test: /(\.jsx?|\.tsx?)(\?|$)/, exclude: /(node_modules|bower_components)/, use: { loader: '...node_modules/babel-loader/lib/index.js', options: [Object] } }, { test: /\.css(\?|$)/, oneOf: [ [Object], [Object] ] }, ... ]
In terms of Webpack loaders, this indicates that we’re passing JS and TS files through the babel-loader
for transpilation except for files in node_modules
or bower_components
(due to the exclude: /(node_modules|bower_components)/
line). Avoiding the transpiling of node_modules
libraries is pretty standard affair in Webpack-land.
However, to get around our issue of modern language features in libraries breaking our Sanity Studio instance, we’ll have Sanity’s babel step transpile the code for these culprit libraries for us. To do so we’ll update our webpack.sanity.js
configuration as follows:
// webpack.sanity.js in root of Sanity project module.exports = function(config) { // 🪄 The libraries/namespaces you want transpiled const librariesToTranspile = ["zod", "@reach"]; // Build out an exclusion regex based on our libraries to transpile. const excludeRegex = new RegExp( `(node_modules|bower_components)\\/(?!${librariesToTranspile.join("|")})` ); const babelRule = config.module.rules[0]; // Adjust our file test to include .mjs files babelRule.test = /(\.jsx?|\.tsx?|.mjs)(\?|$)/; // Override the `exclude` of the babel rule to allow our specified // node_modules libraries to be transpiled. babelRule.exclude = excludeRegex; // And return our modified config. return config; };
This does two key things: it overrides the test
property for our babel rule to allow .mjs
files to be transpiled since these .mjs
files likely include modern language features, and it updates the exclude
property for our babel rule so that we don’t exclude our specified libraries in this step (i.e., we do transpile the specified libraries). The little bit of regular expression magic that makes this work is the “negative lookahead assertion” ?!
in the excludeRegex
value that tells Webpack to exclude all node_modules
imports except the ones that start with node_modules/zod
or node_modules/@reach
(based on our librariesToTranspile
array).
With this little sprinkle of Webpack configuration, we can start using nearly any JS library in our Sanity Studio setup, which unlocks a tremendous amount of potential for providing our content authors with unique and useful workflows!