react-sql-workbench-embedded
Version:
React wrapper component for the sql-workbench-embedded package, which is a library for creating interactive SQL editors in the browser using DuckDB WASM.
698 lines (551 loc) • 16.8 kB
Markdown
# react-sql-workbench-embedded
A React wrapper component for [sql-workbench-embedded](https://github.com/tobilg/sql-workbench-embedded), enabling interactive SQL execution environments powered by DuckDB WASM directly in your React applications.
## Features
- **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
## Installation
### Via npm
```bash
npm install react-sql-workbench-embedded
```
### Via CDN (UMD)
```html
<!-- 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>
```
### Via CDN (ESM with Import Maps)
```html
<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>
```
## Quick Start
### Basic Usage (npm)
```tsx
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 10);"
theme="auto"
editable={true}
/>
);
}
```
### With Provider (Global Configuration)
```tsx
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>
);
}
```
## API Reference
### `SQLWorkbenchEmbedded` Component
#### Props
| 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 |
#### Ref API
```tsx
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 instance
- `getElement()`: Get the container element
### `SQLWorkbenchProvider` Component
Provides global configuration for all SQLWorkbenchEmbedded components.
#### Props
| 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 |
#### Configuration Options
```typescript
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>;
}
```
### `useSQLWorkbench` Hook
Hook to access SQL Workbench context status.
```tsx
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;" />;
}
```
## Usage Examples
### 1. Simple Component
The simplest way to use the component:
```tsx
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>
);
}
```
### 2. With Callbacks
Handle initialization events:
```tsx
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);
}}
/>
);
}
```
### 3. Theme Switching
Allow users to switch themes:
```tsx
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>
);
}
```
### 4. Using Provider for Multiple Instances
Share configuration across multiple workbenches:
```tsx
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>
);
}
```
### 5. Loading DuckDB Extensions
Load extensions for spatial operations:
```tsx
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>
);
}
```
### 6. Read-Only Mode
Create a read-only SQL display:
```tsx
import { SQLWorkbenchEmbedded } from 'react-sql-workbench-embedded';
export default function App() {
return (
<SQLWorkbenchEmbedded
initialCode="SELECT * FROM generate_series(1, 100);"
editable={false}
showOpenButton={false}
/>
);
}
```
### 7. Using Refs
Access the underlying instance:
```tsx
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>
);
}
```
### 8. Loading Data from URLs
Query CSV files from URLs using httpfs extension:
```tsx
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>
);
}
```
### 9. Custom Styling
Add custom styles to the workbench:
```tsx
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'
}}
/>
);
}
```
### 10. Conditional Rendering with Hook
Use the `useSQLWorkbench` hook to handle loading states:
```tsx
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>
);
}
```
### 11. Custom Themes
```tsx
<SQLWorkbenchProvider
config={{
theme: 'ocean',
customThemes: {
ocean: {
extends: 'dark',
config: {
primaryBg: '#0ea5e9',
editorBg: '#1e3a5f',
syntaxKeyword: '#4fc3f7'
}
}
}
}}
>
<SQLWorkbenchEmbedded initialCode="SELECT 1;" />
</SQLWorkbenchProvider>
```
### 12. Multiple Instances with Different Themes
```tsx
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>
);
}
```
## TypeScript Usage
The library is fully typed. Here's how to use types:
```tsx
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
}
```
## Common Patterns
### Error Boundaries
Wrap the component in an error boundary:
```tsx
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>
);
}
```
### Dynamic Code Updates
Update SQL code dynamically:
```tsx
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>
);
}
```
## Tips
1. **Force Remount**: Use the `key` prop to force component remount when you want to reset the workbench completely.
2. **Provider vs. Component Config**: Use Provider for shared configuration across multiple instances. Use component props for instance-specific overrides.
3. **Init Queries**: Use `initQueries` to install and load DuckDB extensions. These run once before any user queries.
4. **Theme Priority**: Component prop > HTML attribute > Provider config. Choose the level that makes sense for your use case.
5. **Performance**: The DuckDB WASM runtime is lazy-loaded only when needed, so initial page load stays fast.
## Development
### Setup
```bash
# 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 build
```
### Project Structure
```
react-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
```
## Requirements
- React 18+ or React 19+
- TypeScript 5.6+ (for development)
- Modern browser with WebAssembly support
## Browser Compatibility
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.
## Distribution Formats
This library is distributed in two formats:
### ESM (ES Module)
- **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
### UMD (Universal Module Definition)
- **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-embedded` internally (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)
## License
MIT