Skip to main content

Vite Integration

Vite is a modern build tool that provides lightning-fast development server and optimized production builds. This guide shows you how to properly configure Vite to work with Jupyter React components.

Live Example

Quick Start

npm install @datalayer/jupyter-react @datalayer/primer-addons
npm install -D vite @vitejs/plugin-react vite-plugin-treat-umd-as-commonjs
npm run dev

Critical Dependencies

Required Runtime Dependencies

{
"dependencies": {
"@datalayer/jupyter-react": "^2.0.0",
"@datalayer/primer-addons": "^1.0.4",
"react": "18.3.1",
"react-dom": "18.3.1"
}
}

Required Dev Dependencies

{
"devDependencies": {
"@vitejs/plugin-react": "^4.6.0",
"vite": "^7.0.4",
"vite-plugin-treat-umd-as-commonjs": "^0.1.4"
}
}
Critical Plugin

vite-plugin-treat-umd-as-commonjs is essential for handling JupyterLab's UMD modules. Without this plugin, you'll encounter module resolution errors.

Vite Configuration

The Vite configuration requires several critical plugins and settings to work properly with Jupyter components:

Complete Configuration

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { treatAsCommonjs } from 'vite-plugin-treat-umd-as-commonjs';

export default defineConfig({
plugins: [
react(),
// Handle JupyterLab UMD modules
treatAsCommonjs(),
// Handle .raw.css imports from JupyterLab
{
name: 'raw-css-as-string',
enforce: 'pre',
async resolveId(source, importer) {
if (source.endsWith('.raw.css') && !source.includes('?raw')) {
const resolved = await this.resolve(source + '?raw', importer, {
skipSelf: true,
});
if (resolved) return resolved.id;
return null;
}
return null;
},
},
// Handle ?text imports (service workers, etc.)
{
name: 'fix-text-query',
enforce: 'pre',
async resolveId(source, importer) {
if (source.includes('?text')) {
const fixed = source.replace('?text', '?raw');
const resolved = await this.resolve(fixed, importer, {
skipSelf: true,
});
if (resolved) return resolved.id;
return fixed;
}
return null;
},
},
],
// Include non-standard assets
assetsInclude: ['**/*.whl', '**/*.raw.css'],
resolve: {
alias: [
{
// Handle webpack-style ~ imports in CSS
find: /^~(.*)$/,
replacement: '$1',
},
],
},
define: {
// Jupyter components expect these globals
global: 'globalThis',
__webpack_public_path__: '""',
},
worker: {
format: 'es',
},
build: {
rollupOptions: {
output: {
// Preserve Python wheel files in pypi/ directory
assetFileNames: assetInfo => {
if (/pypi\//.test(assetInfo.name)) {
return 'pypi/[name][extname]';
}
return 'assets/[name][extname]';
},
},
},
},
optimizeDeps: {
esbuildOptions: {
loader: {
// Treat .whl files as text
'.whl': 'text',
},
},
},
});

Why Each Configuration Matters

1. treatAsCommonjs Plugin ⚠️ Critical

JupyterLab packages use UMD modules which Vite doesn't handle natively. This plugin is absolutely required.

treatAsCommonjs();

Without this: Module resolution errors and runtime crashes.

2. raw-css-as-string Plugin ⚠️ Required

JupyterLab imports CSS files with .raw.css extension to get the CSS as a string (not injected styles).

{
name: 'raw-css-as-string',
enforce: 'pre',
// ... converts .raw.css imports to ?raw queries
}

Without this: CSS loading errors from @jupyterlab/apputils-extension.

3. fix-text-query Plugin ⚠️ Required

Jupyter Lite uses ?text imports for service workers. Vite expects ?raw instead.

{
name: 'fix-text-query',
enforce: 'pre',
// ... converts ?text to ?raw
}

Without this: Service worker loading failures.

4. Tilde Alias Resolution ⚠️ Required

JupyterLab CSS uses webpack-style ~ prefix for npm imports (e.g., ~react-toastify).

resolve: {
alias: [
{
find: /^~(.*)$/,
replacement: '$1',
},
],
}

Without this: CSS import errors like Cannot resolve ~react-toastify.

5. Global Definitions ⚠️ Required

Jupyter components expect specific globals that may not exist in Vite's environment.

define: {
global: 'globalThis',
__webpack_public_path__: '""',
}

Without this: Runtime errors about undefined global or __webpack_public_path__.

6. Asset Handling 🔧 Important

Handle special file types like Python wheels and raw CSS files.

assetsInclude: ['**/*.whl', '**/*.raw.css'],
optimizeDeps: {
esbuildOptions: {
loader: { '.whl': 'text' },
},
}

HTML Setup

Add these scripts to your index.html before loading your app:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Jupyter React Vite</title>

<!-- Required for IPyWidgets -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"></script>
<script type="module">
globalThis.__webpack_public_path__ = '';
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
IPyWidgets Requirement

RequireJS is essential for IPyWidgets to load widget modules dynamically. Without it, custom widgets won't render.

Common Issues and Solutions

Module Resolution Errors

Problem: Cannot find module '@jupyterlab/...'

Solution: Ensure vite-plugin-treat-umd-as-commonjs is installed and configured.

CSS Import Errors

Problem: Cannot resolve ~react-toastify or similar CSS imports

Solution: Add the tilde alias in resolve.alias configuration.

Service Worker Errors

Problem: Cannot load service-worker?text

Solution: Add the fix-text-query plugin.

IPyWidgets Not Rendering

Problem: Custom widgets show as "Loading widget..." forever

Solution: Add RequireJS script in your HTML.

Production Build Fails

Problem: Build succeeds but runtime errors in production

Solution: Check that all plugins have enforce: 'pre' and verify global definitions.

Development Workflow

# Development server with HMR
npm run dev

# Production build
npm run build

# Preview production build
npm run preview

Example Application

import { JupyterReactTheme, Cell, useJupyter } from '@datalayer/jupyter-react';

function App() {
const { defaultKernel } = useJupyter({
startDefaultKernel: true,
});

return (
<JupyterReactTheme>
{defaultKernel && (
<Cell kernel={defaultKernel} source="print('Hello from Jupyter!')" />
)}
</JupyterReactTheme>
);
}

export default App;

Performance Optimization

Code Splitting

Vite automatically splits vendor code. For Jupyter components:

build: {
rollupOptions: {
output: {
manualChunks: {
'jupyter-core': ['@jupyterlab/services', '@jupyterlab/coreutils'],
'jupyter-ui': ['@datalayer/jupyter-react'],
},
},
},
}

Tree Shaking

Import only what you need:

// ✅ Good - tree-shakeable
import { Cell, Notebook } from '@datalayer/jupyter-react';

// ❌ Bad - imports everything
import * as JupyterReact from '@datalayer/jupyter-react';

Troubleshooting Checklist

Before asking for help, verify:

  • vite-plugin-treat-umd-as-commonjs is installed and in plugins array
  • All three custom plugins (raw-css-as-string, fix-text-query) are configured with enforce: 'pre'
  • Tilde alias is configured in resolve.alias
  • Global definitions (global, __webpack_public_path__) are set
  • RequireJS is loaded in HTML before your app
  • React 18.3.1+ is installed (not 19.x which has breaking changes)

See Also