A React wrapper component for sql-workbench-embedded, enabling interactive SQL execution environments powered by DuckDB WASM directly in your React applications.
- React 18 & 19 Compatible: Works with both React 18 and React 19
- Zero Backend Required: All SQL execution happens in the browser via DuckDB WASM
- Type-Safe: Full TypeScript support with comprehensive type definitions
- Flexible API: Use as a simple component or with global configuration via Context Provider
- Customizable: Support for themes (light/dark/auto), custom themes, and extensive configuration options
- Modern Development: Built with Vite, tested with Vitest
- Privacy-Focused: No data leaves the browser
- Multiple Distribution Formats: Available as ESM and UMD builds for maximum compatibility
- CDN-Ready: Can be used directly from CDN without build tools
npm install react-sql-workbench-embedded<!-- React and ReactDOM (use React 18 for UMD compatibility) -->
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@18/umd/react-dom.production.min.js"></script>
<!-- React SQL Workbench Embedded -->
<script src="https://cdn.jsdelivr.net/npm/react-sql-workbench-embedded/dist/react-sql-workbench-embedded.umd.js"></script>
<script>
const { SQLWorkbenchEmbedded } = window.SQLWorkbenchEmbedded;
// Use the component...
</script><script type="importmap">
{
"imports": {
"react": "https://cdn.jsdelivr.net/npm/react@18/+esm",
"react-dom": "https://cdn.jsdelivr.net/npm/react-dom@18/+esm",
"@duckdb/duckdb-wasm": "https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.31.1-dev1.0/+esm",
"react-sql-workbench-embedded": "https://cdn.jsdelivr.net/npm/react-sql-workbench-embedded/dist/react-sql-workbench-embedded.esm.js"
}
}
</script>
<script type="module">
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
// Use the component...
</script>import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 10);"
theme="auto"
editable={true}
/>
);
}import { SQLWorkbenchProvider, SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
function App() {
return (
<SQLWorkbenchProvider
config={{
theme: 'dark',
editable: true,
initQueries: [
'INSTALL spatial',
'LOAD spatial'
]
}}
>
<SQLWorkbenchEmbedded
initialCode="SELECT ST_AsText(ST_Point(1, 2)) as point;"
/>
</SQLWorkbenchProvider>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
initialCode |
string |
'' |
Initial SQL code to display in the workbench |
theme |
'light' | 'dark' | 'auto' | string |
'auto' |
Theme for the workbench |
editable |
boolean |
true |
Whether the SQL editor is editable |
showOpenButton |
boolean |
true |
Show "Open in SQL Workbench" button |
className |
string |
'' |
Custom className for the container element |
style |
React.CSSProperties |
- | Custom styles for the container element |
onReady |
(instance) => void |
- | Callback when workbench is ready |
onError |
(error) => void |
- | Callback when initialization fails |
import { useRef } from 'react';
import { SQLWorkbenchEmbedded, type SQLWorkbenchEmbeddedRef } from 'react-sql-workbench-embedded';
function App() {
const ref = useRef<SQLWorkbenchEmbeddedRef>(null);
return (
<SQLWorkbenchEmbedded
ref={ref}
initialCode="SELECT 1;"
/>
);
}Methods available via ref:
getInstance(): Get the underlying SQLWorkbench instancegetElement(): Get the container element
Provides global configuration for all SQLWorkbenchEmbedded components.
| Prop | Type | Description |
|---|---|---|
config |
SQLWorkbenchConfig |
Global configuration options |
children |
ReactNode |
Child components |
onReady |
() => void |
Callback when SQL Workbench is ready |
onError |
(error) => void |
Callback when initialization fails |
interface SQLWorkbenchConfig {
selector?: string;
baseUrl?: string;
theme?: 'light' | 'dark' | 'auto' | string;
autoInit?: boolean;
duckdbVersion?: string;
duckdbCDN?: string;
editable?: boolean;
showOpenButton?: boolean;
initQueries?: string[];
customThemes?: Record<string, CustomThemeConfig>;
}Hook to access SQL Workbench context status.
import { useSQLWorkbench } from 'react-sql-workbench-embedded';
function MyComponent() {
const { isReady, error } = useSQLWorkbench();
if (error) return <div>Error: {error.message}</div>;
if (!isReady) return <div>Loading...</div>;
return <SQLWorkbenchEmbedded initialCode="SELECT 1;" />;
}The simplest way to use the component:
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<div>
<h1>My SQL Workbench</h1>
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 10);"
/>
</div>
);
}Handle initialization events:
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT 1 + 1 as result;"
onReady={(instance) => {
console.log('Workbench ready!', instance);
}}
onError={(error) => {
console.error('Failed to initialize:', error);
}}
/>
);
}Allow users to switch themes:
import { useState } from 'react';
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
const [theme, setTheme] = useState<'light' | 'dark' | 'auto'>('auto');
return (
<div>
<select value={theme} onChange={(e) => setTheme(e.target.value)}>
<option value="auto">Auto</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
<SQLWorkbenchEmbedded
key={theme} // Force remount on theme change
initialCode="SELECT 'Hello World' as message;"
theme={theme}
/>
</div>
);
}Share configuration across multiple workbenches:
import { SQLWorkbenchProvider, SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchProvider
config={{
theme: 'dark',
editable: true
}}
>
<div>
<h2>Query 1</h2>
<SQLWorkbenchEmbedded
initialCode="SELECT 'First Query' as title;"
/>
<h2>Query 2</h2>
<SQLWorkbenchEmbedded
initialCode="SELECT 'Second Query' as title;"
/>
</div>
</SQLWorkbenchProvider>
);
}Load extensions for spatial operations:
import { SQLWorkbenchProvider, SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchProvider
config={{
initQueries: [
'INSTALL spatial',
'LOAD spatial'
]
}}
>
<SQLWorkbenchEmbedded
initialCode={`
SELECT
ST_AsText(ST_Point(1.5, 2.5)) as point,
ST_AsText(ST_MakePolygon(
'LINESTRING(0 0, 0 1, 1 1, 1 0, 0 0)'
)) as polygon;
`}
/>
</SQLWorkbenchProvider>
);
}Create a read-only SQL display:
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 100);"
editable={false}
showOpenButton={false}
/>
);
}Access the underlying instance:
import { useRef } from 'react';
import { SQLWorkbenchEmbedded, SQLWorkbenchEmbeddedRef } from 'react-sql-workbench-embedded';
export default function App() {
const workbenchRef = useRef<SQLWorkbenchEmbeddedRef>(null);
const handleClick = () => {
const instance = workbenchRef.current?.getInstance();
console.log('Current instance:', instance);
};
return (
<div>
<button onClick={handleClick}>
Get Instance
</button>
<SQLWorkbenchEmbedded
ref={workbenchRef}
initialCode="SELECT 1;"
/>
</div>
);
}Query CSV files from URLs using httpfs extension:
import { SQLWorkbenchProvider, SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchProvider
config={{
initQueries: [
'INSTALL httpfs',
'LOAD httpfs'
]
}}
>
<SQLWorkbenchEmbedded
initialCode={`
SELECT *
FROM read_csv_auto('https://example.com/data.csv')
LIMIT 10;
`}
/>
</SQLWorkbenchProvider>
);
}Add custom styles to the workbench:
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 5);"
className="my-custom-workbench"
style={{
border: '2px solid #3498db',
borderRadius: '8px',
padding: '1rem'
}}
/>
);
}Use the useSQLWorkbench hook to handle loading states:
import { SQLWorkbenchProvider, SQLWorkbenchEmbedded, useSQLWorkbench } from 'react-sql-workbench-embedded';
function WorkbenchContent() {
const { isReady, error } = useSQLWorkbench();
if (error) {
return <div>Error: {error.message}</div>;
}
if (!isReady) {
return <div>Loading SQL Workbench...</div>;
}
return (
<SQLWorkbenchEmbedded
initialCode="SELECT 'Ready!' as status;"
/>
);
}
export default function App() {
return (
<SQLWorkbenchProvider>
<WorkbenchContent />
</SQLWorkbenchProvider>
);
}<SQLWorkbenchProvider
config={{
theme: 'ocean',
customThemes: {
ocean: {
extends: 'dark',
config: {
primaryBg: '#0ea5e9',
editorBg: '#1e3a5f',
syntaxKeyword: '#4fc3f7'
}
}
}
}}
>
<SQLWorkbenchEmbedded initialCode="SELECT 1;" />
</SQLWorkbenchProvider>function App() {
return (
<div>
<h2>Light Theme</h2>
<SQLWorkbenchEmbedded
initialCode="SELECT 'Hello' as greeting;"
theme="light"
/>
<h2>Dark Theme</h2>
<SQLWorkbenchEmbedded
initialCode="SELECT 'World' as subject;"
theme="dark"
/>
</div>
);
}The library is fully typed. Here's how to use types:
import { useState, useRef } from 'react';
import type {
SQLWorkbenchEmbeddedRef,
SQLWorkbenchConfig,
Theme
} from 'react-sql-workbench-embedded';
const config: SQLWorkbenchConfig = {
theme: 'dark',
editable: true,
initQueries: ['INSTALL spatial']
};
function MyComponent() {
const [theme, setTheme] = useState<Theme>('auto');
const ref = useRef<SQLWorkbenchEmbeddedRef>(null);
// ... rest of component
}Wrap the component in an error boundary:
import { Component, ReactNode } from 'react';
class ErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
constructor(props: { children: ReactNode }) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h2>Something went wrong.</h2>;
}
return this.props.children;
}
}
export default function App() {
return (
<ErrorBoundary>
<SQLWorkbenchEmbedded initialCode="SELECT 1;" />
</ErrorBoundary>
);
}Update SQL code dynamically:
import { useState } from 'react';
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
const [code, setCode] = useState("SELECT 1;");
const queries = [
"SELECT * FROM generate_series(1, 10);",
"SELECT DATE '2024-01-01' + INTERVAL (n) DAY FROM generate_series(0, 6) t(n);",
"SELECT 'Hello', 'World';"
];
return (
<div>
<div>
{queries.map((query, idx) => (
<button key={idx} onClick={() => setCode(query)}>
Query {idx + 1}
</button>
))}
</div>
<SQLWorkbenchEmbedded
key={code} // Force remount when code changes
initialCode={code}
/>
</div>
);
}-
Force Remount: Use the
keyprop to force component remount when you want to reset the workbench completely. -
Provider vs. Component Config: Use Provider for shared configuration across multiple instances. Use component props for instance-specific overrides.
-
Init Queries: Use
initQueriesto install and load DuckDB extensions. These run once before any user queries. -
Theme Priority: Component prop > HTML attribute > Provider config. Choose the level that makes sense for your use case.
-
Performance: The DuckDB WASM runtime is lazy-loaded only when needed, so initial page load stays fast.
# Clone the repository
git clone https://github.com/tobilg/react-sql-workbench-embedded.git
cd react-sql-workbench-embedded
# Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Run tests with UI
npm run test:ui
# Build the library
npm run buildreact-sql-workbench-embedded/
├── src/
│ ├── components/
│ │ ├── SQLWorkbenchEmbedded.tsx # Main component
│ │ ├── SQLWorkbenchProvider.tsx # Context provider
│ │ └── __tests__/ # Component tests
│ ├── demo/ # Demo application
│ │ ├── App.tsx
│ │ └── main.tsx
│ ├── types.ts # TypeScript types
│ └── index.ts # Main entry point
├── vite.config.ts # Vite configuration
├── vitest.config.ts # Vitest configuration
└── package.json
- React 18+ or React 19+
- TypeScript 5.6+ (for development)
- Modern browser with WebAssembly support
This library requires a modern browser with support for:
- ES Modules (for ESM builds)
- WebAssembly
- Import Maps (for ESM CDN usage)
For the UMD build, all major modern browsers are supported without additional configuration.
This library is distributed in two formats:
- File:
dist/react-sql-workbench-embedded.esm.js(~32 KB, ~10 KB gzipped) - Use case: Modern build tools (Vite, Webpack, etc.) or direct browser usage with import maps
- Dependencies: Requires React, ReactDOM, and @duckdb/duckdb-wasm to be available
- File:
dist/react-sql-workbench-embedded.umd.js(~31 KB, ~10 KB gzipped) - Use case: Direct browser usage via
<script>tags or legacy module systems - Global: Exposed as
window.SQLWorkbenchEmbedded - Dependencies: Requires React and ReactDOM to be loaded first (use React 18 for UMD compatibility)
Both formats:
- Bundle
sql-workbench-embeddedinternally (no manual installation needed) - Externalize React and ReactDOM (expected as peer dependencies)
- Use classic JSX runtime for maximum compatibility
- Include no polyfills (work directly in modern browsers)
MIT