Adding a script tag to index.html to avoid stale js bundle and integrate with import-meta-env tooling

1 replies
Last updated: Feb 7, 2023
Is there a way to append a script tag to the index.html?I tried writing my own little vite plugin to
transform the html , but it doesn't seem to be running?
I would like to add a piece of code directly to the index.html, so I don't risk having it get stale with the js bundle.

Perhaps there is a better way to do that, but I also didn't have luck with adding a script to the
static
dir either.
My goal is to integrate with
import-meta-env tooling to allow for runtime env vars while self hosting.
AI Update

Based on the Sanity documentation I found, here's what you need to know about customizing the Sanity Studio's HTML and Vite configuration:

Customizing Vite Configuration in Sanity Studio

Sanity Studio v3+ uses Vite as its build tool, and you can extend the built-in Vite configuration through the Sanity CLI config file.

To customize the Vite configuration and add script tags to index.html, create or modify sanity.cli.js (or sanity.cli.ts) in your Studio project root:

// sanity.cli.js
import {defineCliConfig} from 'sanity/cli'

export default defineCliConfig({
  api: {
    // your API config...
  },
  vite: {
    // Your Vite configuration overrides go here
    plugins: [
      // Add your custom Vite plugins here
    ],
  },
})

Adding Script Tags for Runtime Environment Variables with import-meta-env

For your specific use case with import-meta-env for runtime environment variables while self-hosting, you can use Vite's transformIndexHtml hook:

// sanity.cli.js
import {defineCliConfig} from 'sanity/cli'

export default defineCliConfig({
  api: {
    projectId: 'your-project-id',
    dataset: 'your-dataset',
  },
  vite: {
    plugins: [
      {
        name: 'inject-runtime-env',
        transformIndexHtml(html) {
          return html.replace(
            '</head>',
            `<script src="/env.js"></script></head>`
          )
        },
      },
    ],
  },
})

Alternatively, if you're using the @import-meta-env/unplugin package directly:

// sanity.cli.js
import {defineCliConfig} from 'sanity/cli'
import importMetaEnv from '@import-meta-env/unplugin'

export default defineCliConfig({
  api: {
    projectId: 'your-project-id',
    dataset: 'your-dataset',
  },
  vite: {
    plugins: [
      importMetaEnv.vite({
        example: '.env.example',
        env: '.env',
      }),
    ],
  },
})

Why Your Vite Plugin Might Not Be Running

If your custom Vite plugin with transformIndexHtml isn't running, it's likely because:

  1. Wrong configuration location: Make sure you're adding it to sanity.cli.js, not sanity.config.js (which is for Studio configuration, not build configuration)
  2. Plugin order: Sanity's internal plugins might be interfering - try different hook orders
  3. Development vs. production: The transformIndexHtml plugin runs during both dev and build, but check both modes

Testing Your Configuration

After adding your Vite configuration:

  • For development: Run npm run dev or npx sanity dev
  • For production build: Run npm run build or npx sanity build
  • Check the built dist/index.html to verify your script tag was injected

About the Static Directory

While Sanity Studio does support a static directory for assets, you cannot directly modify the index.html template. The Vite plugin approach with transformIndexHtml is the correct way to inject custom script tags.

The key insight is that Sanity Studio uses Vite under the hood, so any standard Vite plugin approach should work when properly configured in sanity.cli.js. You can learn more about Vite configuration options in their documentation.

In case anyone deals with this in the future, i ended up writing a custom rollup plugin that wrote the script to the head of the
index.html
that the sanity cli created:
// sanity.cli.ts
const importMetaEnvSpecialExpression = (options?: {
  out: string
}): PluginOption => {
  const out = options?.out ?? "dist"
  return {
    name: "build end",
    enforce: "post",
    async writeBundle(opts, things) {
      const html =
        things["index.html"].type === "asset" ? things["index.html"] : null
      if (!html || typeof html.source !== "string") return
      const parsedHtml = parse(html.source)
      const importMetaEnvSpecialScript = `<script>globalThis.import_meta_env = JSON.parse('"import_meta_env_placeholder"');</script>`
      parsedHtml
        .querySelector("head")
        ?.insertAdjacentHTML("afterbegin", importMetaEnvSpecialScript)

      await fs.writeFile(
        path.resolve(process.cwd(), out, "index.html"),
        parsedHtml.toString()
      )
    },
  }
}

export default defineCliConfig({
  // config options
    vite: (viteConfig: UserConfig): UserConfig => ({
    ...viteConfig,
    plugins: [
      ...(viteConfig.plugins ?? []),
      // plugin i was trying to integrate with
      ImportMetaEnvPlugin.vite({
        example: ".env.example.public",
      }),
      // custom plugin from above
      importMetaEnvSpecialExpression(),
    ],
  }),
})

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?