Third-party Packages in Sanity Studio V2

November 15, 2022

(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:

Example of default Sanity Studio interface

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.

Example of a custom Sanity Studio input component

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!

Related Posts

Getting Started with (and Surviving) Node.js ESM

November 9, 2021
The Node.js ecosystem is shifting towards ESM. We examine what lies in store for application and library authors, learn some of challenges and opportunities that lie ahead for Node.js developers, and provide tips and tricks for surviving the bumpy transition ride ahead.

The awkward valley to ESM: Node.js, Victory, and D3

June 15, 2022
We continue our examination of the shift to ESM on Node.js and broader ramifications for the ecosystem. In this post, we look at the challenges and lessons in handling ESM-only dependencies in our Victory charting library.

Green with Envy: Tracing Network Calls Across Your Application Stack

November 20, 2023
Envy is a zero config network tracing and telemetry viewer for development. It allows engineers to have a live view into the network requests your application stack is making during local development.