Skip to content

Getting Started

Terminal window
npm install -D @woowabros/vite-plugin-critical-script
# or
yarn add -D @woowabros/vite-plugin-critical-script
# or
pnpm add -D @woowabros/vite-plugin-critical-script
  1. Register the plugin in vite.config.ts.
import { defineConfig } from 'vite'
import { criticalScriptPlugin } from '@woowabros/vite-plugin-critical-script'
export default defineConfig({
plugins: [criticalScriptPlugin({ outputSizeLimit: 8192 })],
})
  1. Write the code you want to run before the main bundle in a separate file.
home.critical.ts
window.__home = fetch('/api/home').then((r) => r.json())
  1. Import it from a component with the ?as-critical-script suffix.
import CriticalScript from './home.critical?as-critical-script'
export default function Home() {
return (
<>
<CriticalScript />
<Page />
</>
)
}

At build time, home.critical.ts is compiled and minified by esbuild and inlined into the HTML as a <script> tag. See the API Reference for plugin options such as define, and Use Cases for common patterns.

Suppose you have this home.critical.ts.

performance.mark('critical-start')
window.__home = fetch('/api/home').then((r) => r.json())

After the build, it is inlined into the HTML like this (with esbuild minification applied).

<script data-size="98">(()=>{performance.mark("critical-start");window.__home=fetch("/api/home").then(e=>e.json());})();</script>
  • The script is wrapped in an IIFE, so it does not pollute the global scope.
  • The data-size attribute lets you check the post-minification byte size at a glance, which is useful for debugging and monitoring.

For your IDE and build tooling to recognize the type definitions for ?as-critical-script imports, add the package name to compilerOptions.types in tsconfig.json.

{
"compilerOptions": {
"types": ["@woowabros/vite-plugin-critical-script"]
}
}

With this setting, the default export of import CriticalScript from './foo.critical?as-critical-script' is inferred as a React component that accepts the standard HTML <script> attributes.