@deckyfx/dioxus-react-bridge
Version:
React hooks and components for Dioxus IPC communication - lightweight bridge between React and Rust
667 lines (503 loc) • 17.7 kB
Markdown
# dioxus-react-bridge
React hooks and components for seamless IPC communication with Dioxus Rust backend.
[](https://www.npmjs.com/package/dioxus-react-bridge)
[](https://opensource.org/licenses/MIT)
## Features
- ✅ **HTTP-like IPC API** - Request/response pattern with methods, URLs, headers, bodies
- ✅ **Event Streaming** - Real-time events from Rust → React with pub/sub pattern
- ✅ **Streaming Tasks** - Progress tracking and chunked data transfer for long-running operations
- ✅ **Zero Runtime Dependencies** - Lightweight EventEmitter replaces RxJS (~50-80KB saved)
- ✅ **TypeScript First** - Full type safety with comprehensive type definitions
- ✅ **Global Type Definitions** - Complete types for all Dioxus-injected APIs (`window.dioxusBridge`, `window.ipc`, etc.)
- ✅ **Build Utility** - Configurable build tool with Tailwind support, watch mode, and auto-copy to Dioxus assets
- ✅ **React Hooks** - Idiomatic React patterns with hooks for all IPC operations
- ✅ **Platform Agnostic** - Works on desktop (webview), web (WASM), and mobile
## Installation
```bash
# Using bun
bun add dioxus-react-bridge react react-dom
# Using npm
npm install dioxus-react-bridge react react-dom
# Using yarn
yarn add dioxus-react-bridge react react-dom
```
## Quick Start
### 1. TypeScript Setup (Recommended)
Import global type definitions in your entry file for full TypeScript support of Dioxus-injected APIs:
```tsx
// src/index.tsx or src/main.tsx
import '@deckyfx/dioxus-react-bridge/global';
```
This provides complete type definitions for:
- `window.dioxusBridge` - Main IPC bridge
- `window.ipc` - Low-level IPC channel
- `window.interpreter` - Dioxus virtual DOM interpreter
- `window.showDXToast()`, `window.scheduleDXToast()`, `window.closeDXToast()` - Toast notifications
**Alternative: Add to tsconfig.json**
```json
{
"compilerOptions": {
"types": ["@deckyfx/dioxus-react-bridge/global"]
}
}
```
### 2. Wrap Your App
```tsx
import { IPCReady, RustIPCProvider } from '@deckyfx/dioxus-react-bridge';
import '@deckyfx/dioxus-react-bridge/global'; // TypeScript types
function App() {
return (
<IPCReady>
<RustIPCProvider>
<MyApp />
</RustIPCProvider>
</IPCReady>
);
}
```
### 3. Make IPC Calls
```tsx
import { useRustIPC } from '@deckyfx/dioxus-react-bridge';
function Calculator() {
const { fetch } = useRustIPC();
const [result, setResult] = useState(null);
const calculateFibonacci = async () => {
const response = await fetch('ipc://calculator/fibonacci?number=10');
setResult(response.body.result);
};
return (
<div>
<button onClick={calculateFibonacci}>Calculate</button>
{result && <div>Result: {result}</div>}
</div>
);
}
```
### 4. Listen to Rust Events
```tsx
import { useRustEventListener } from '@deckyfx/dioxus-react-bridge';
function HeartbeatMonitor() {
const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');
return <div>Heartbeat: {heartbeat?.count || 'Waiting...'}</div>;
}
```
### 5. Direct Access to Dioxus APIs (with Types!)
```tsx
// Thanks to global types, you get full IntelliSense and type checking
async function loadAsset() {
// window.dioxusBridge is fully typed
const response = await window.dioxusBridge.fetch('ipc://assets/image');
console.log(response.body);
}
function showNotification() {
// Toast functions are fully typed
window.showDXToast?.('Success', 'Operation completed!', 'success', 3000);
}
```
## Complete Workflow Guide
### Step 1: Create Dioxus App
```bash
dx new my-app
cd my-app
```
### Step 2: Add Rust Crates
Add to `Cargo.toml`:
```toml
[dependencies]
dioxus = "0.7.0"
dioxus-ipc-bridge = { path = "../dioxus-ipc-bridge" }
dioxus-ipc-bridge-macros = { path = "../dioxus-ipc-bridge-macros" }
dioxus-react-integration = { path = "../dioxus-react-integration" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
```
### Step 3: Set Up Rust Side
```rust
use dioxus::prelude::*;
use dioxus_ipc_bridge::prelude::*;
fn main() {
// Initialize IPC bridge
let bridge = IpcBridge::builder()
.timeout(std::time::Duration::from_secs(30))
.build();
bridge.initialize();
// Launch Dioxus app
dioxus::launch(app);
}
fn app() -> Element {
rsx! {
// Your Dioxus UI
ReactContainer {
bundle: asset!("/assets/react/bundle.js"),
mount_id: "react-root"
}
}
}
```
### Step 4: Create React App
```bash
# Initialize bun project
mkdir react-app && cd react-app
bun init
# Install dependencies
bun add react react-dom dioxus-react-bridge
# Install dev dependencies
bun add -d @types/react @types/react-dom typescript
```
### Step 5: Write React App
Create `src/index.tsx`:
```tsx
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { IPCReady, RustIPCProvider, useRustIPC } from '@deckyfx/dioxus-react-bridge';
import '@deckyfx/dioxus-react-bridge/global'; // Import global types
function App() {
const { fetch } = useRustIPC();
const [result, setResult] = useState(null);
const handleClick = async () => {
const response = await fetch('ipc://endpoint');
setResult(response.body);
};
return (
<div>
<button onClick={handleClick}>Call Rust</button>
{result && <div>{JSON.stringify(result)}</div>}
</div>
);
}
const root = document.getElementById('react-root');
if (root) {
createRoot(root).render(
<IPCReady>
<RustIPCProvider>
<App />
</RustIPCProvider>
</IPCReady>
);
}
```
### Step 6: Build React Bundle
Create `build.ts` using the built-in build utility:
```typescript
import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';
await buildForDioxus({
// Entry point for your React app
entrypoint: './src/index.tsx',
// Where to output the bundle
outdir: './dist',
// Path to your Dioxus app's assets directory
dioxusAssetsPath: '../my-dioxus-app/assets/react',
// Minify the output
minify: true,
// Optional: Tailwind CSS processing
tailwind: {
input: './src/App.css',
output: './src/App.processed.css',
},
// Optional: Verbose logging
verbose: true,
});
```
**Minimal Configuration:**
```typescript
import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';
// Just specify where to copy the bundle
await buildForDioxus({
dioxusAssetsPath: '../my-dioxus-app/assets/react'
});
```
Run build:
```bash
bun run build.ts
```
**Development Watch Mode:**
Create `watch.ts` for automatic rebuilding during development:
```typescript
import { watchForDioxus } from '@deckyfx/dioxus-react-bridge/build';
await watchForDioxus({
dioxusAssetsPath: '../my-dioxus-app/assets/react',
verbose: true
});
```
Run watch mode:
```bash
bun run watch.ts
```
### Step 7: Run Your App
The build utility automatically copies the bundle to your Dioxus assets directory (if `dioxusAssetsPath` is specified).
If you didn't use `dioxusAssetsPath`, manually copy:
```bash
cp react-app/dist/bundle.js ../my-app/assets/react/bundle.js
```
### Step 8: Start Dioxus
```bash
cd my-app
dx serve
```
## API Reference
### Build Utility
The package includes a configurable build utility to simplify bundling React apps for Dioxus.
#### `buildForDioxus(config?)`
Main build function that handles Tailwind CSS processing, bundling, and copying to Dioxus assets.
```typescript
import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';
await buildForDioxus({
entrypoint: './src/index.tsx',
outdir: './dist',
outputFilename: 'bundle.js',
dioxusAssetsPath: '../my-dioxus-app/assets/react',
minify: true,
tailwind: {
input: './src/App.css',
output: './src/App.processed.css',
options: '--minify'
},
loaders: {
'.png': 'file',
'.svg': 'file'
},
verbose: true
});
```
**Configuration Options:**
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `entrypoint` | `string` | `'./src/index.tsx'` | Entry point for your React app |
| `outdir` | `string` | `'./dist'` | Output directory |
| `outputFilename` | `string` | `'bundle.js'` | Output filename |
| `dioxusAssetsPath` | `string` | `undefined` | Path to Dioxus assets directory |
| `minify` | `boolean` | `true` | Enable minification |
| `target` | `string` | `'browser'` | Build target (`'browser'`, `'bun'`, `'node'`) |
| `tailwind` | `object` | `undefined` | Tailwind CSS configuration |
| `tailwind.input` | `string` | `'./src/App.css'` | Tailwind input file |
| `tailwind.output` | `string` | `'./src/App.processed.css'` | Tailwind output file |
| `tailwind.options` | `string` | `'--minify'` | Tailwind CLI options |
| `loaders` | `object` | See defaults | Custom file loaders |
| `external` | `string[]` | `[]` | Packages to exclude from bundle |
| `define` | `object` | `{}` | Environment variables to define |
| `customBunConfig` | `object` | `{}` | Custom Bun build configuration |
| `verbose` | `boolean` | `false` | Enable verbose logging |
**Default Loaders:**
- `.css` → `'text'` (inlined as string)
- `.svg`, `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp` → `'file'` (base64 encoded)
- `.woff`, `.woff2`, `.ttf`, `.otf`, `.eot` → `'file'` (base64 encoded)
#### `watchForDioxus(config?)`
Watch mode for automatic rebuilding during development.
```typescript
import { watchForDioxus } from '@deckyfx/dioxus-react-bridge/build';
await watchForDioxus({
dioxusAssetsPath: '../my-dioxus-app/assets/react',
verbose: true
});
```
**Features:**
- Watches all files in `src/` directory recursively
- Automatic rebuild on file changes
- Ignores `node_modules` and processed files
- Initial build on start
#### `createBuildConfig(config)`
Create a reusable build configuration with defaults applied.
```typescript
import { createBuildConfig, buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';
const myConfig = createBuildConfig({
dioxusAssetsPath: '../my-app/assets/react',
minify: process.env.NODE_ENV === 'production',
verbose: process.env.DEBUG === 'true'
});
await buildForDioxus(myConfig);
```
### Components
#### `<IPCReady>`
Suspense-like component that waits for `window.dioxusBridge` to be initialized.
```tsx
<IPCReady
fallback={<Spinner />}
onReady={() => console.log('IPC ready!')}
timeout={10000}
>
<App />
</IPCReady>
```
**Props:**
- `fallback?: ReactNode` - Custom loading UI
- `onReady?: () => void` - Callback when IPC is ready
- `timeout?: number` - Timeout in ms (default: 10000)
#### `<RustIPCProvider>`
Context provider for IPC functionality.
```tsx
<RustIPCProvider>
<App />
</RustIPCProvider>
```
### Hooks
#### `useRustIPC()`
Main hook for HTTP-like IPC requests.
```tsx
const { fetch, isReady } = useRustIPC();
// GET request
const response = await fetch('ipc://endpoint');
// POST request
const response = await fetch('ipc://endpoint', {
method: 'POST',
body: { key: 'value' }
});
```
**Returns:**
- `fetch<T>(url, options?)` - HTTP-like fetch function
- `isReady: boolean` - Whether IPC is ready
#### `useRustEventListener(channel)`
Listen to events from Rust.
```tsx
const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');
```
**Parameters:**
- `channel: string` - Event channel name
**Returns:**
- `T | null` - Latest event data
#### `useRustEventEmitter(channel)`
Emit events to a channel.
```tsx
const emit = useRustEventEmitter<{ message: string }>('notification');
emit({ message: 'Hello!' });
```
**Parameters:**
- `channel: string` - Event channel name
**Returns:**
- `(data: T) => void` - Emit function
#### `useStreamingTask(options)`
Manage long-running streaming tasks with progress tracking.
```tsx
const { start, progress, result, isRunning, cancel } = useStreamingTask({
onProgress: (p) => console.log(`Progress: ${p.percent}%`),
onComplete: (result) => console.log('Done!', result),
});
// Start task
await start('ipc://process/video', {
method: 'POST',
body: { videoId: '123' }
});
```
**Options:**
- `onProgress?: (progress) => void`
- `onChunk?: (chunk) => void`
- `onComplete?: (result) => void`
- `onError?: (error) => void`
- `autoReassemble?: boolean`
**Returns:**
- `taskId: string | null`
- `isRunning: boolean`
- `progress: StreamingProgress | null`
- `chunks: StreamingChunk[]`
- `result: T | null`
- `error: Error | null`
- `start: (url, options) => Promise<void>`
- `cancel: () => void`
- `reset: () => void`
### Type Exports
#### Main Types
```typescript
import type {
IpcFetchOptions,
IpcResponse,
IpcErrorResponse,
StreamingProgress,
StreamingChunk,
StreamingTaskOptions,
StreamingTaskState,
DioxusBridge,
IPCBridgeInstance,
} from '@deckyfx/dioxus-react-bridge';
```
#### Global Type Definitions
Import global type definitions for complete TypeScript support of Dioxus-injected APIs:
```typescript
// Import in your entry file
import '@deckyfx/dioxus-react-bridge/global';
// Now you get full type safety for:
window.dioxusBridge.fetch('ipc://endpoint'); // ✓ Fully typed
window.ipc.postMessage(JSON.stringify({...})); // ✓ Fully typed
window.showDXToast?.('Title', 'Message', 'success', 3000); // ✓ Fully typed
window.interpreter?.scrollTo(id, options); // ✓ Fully typed (internal API)
```
**Available Global Types:**
- **`window.dioxusBridge`** - Main IPC bridge with HTTP-like fetch API
- `fetch<T>(url, options?)` - Make IPC requests
- `rustEmit(channel, data)` - Emit events to React (called by Rust)
- `IPCBridge` - Event system for pub/sub
- `ipc.send(data)` - Low-level send
- `ipc.hasIPCBridge()` - Check for event system
- **`window.ipc`** - Low-level IPC channel
- `postMessage(message)` - Send raw IPC message to Rust
- **`window.interpreter`** - Dioxus virtual DOM interpreter (internal, advanced use only)
- DOM manipulation: `scroll`, `scrollTo`, `setFocus`, `getClientRect`
- Scroll getters: `getScrollHeight`, `getScrollLeft`, `getScrollTop`, `getScrollWidth`
- Edit management: `enqueueBytes`, `flushQueuedBytes`, `rafEdits`
- Drag & drop: `handleWindowsDragDrop`, `handleWindowsDragOver`, `handleWindowsDragLeave`
- **`window.showDXToast()`** - Show toast notification
- **`window.scheduleDXToast()`** - Schedule toast after reload
- **`window.closeDXToast()`** - Close current toast
**Example with Full Type Safety:**
```typescript
import '@deckyfx/dioxus-react-bridge/global';
// All of these have full IntelliSense and type checking!
async function example() {
// Main IPC bridge
const response = await window.dioxusBridge.fetch<{ result: number }>(
'ipc://calculator/fibonacci?number=10'
);
console.log(response.body.result); // ✓ TypeScript knows this is a number
// Toast notifications
window.showDXToast?.('Success', 'Calculation complete!', 'success', 3000);
// Event system
window.dioxusBridge.IPCBridge?.on('rust:heartbeat').subscribe({
next: (data) => console.log(data)
});
}
```
## Architecture
```
┌─────────────────────────────────────┐
│ React App (Your Code) │
│ - useRustIPC(), useRustEventListener │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ dioxus-react-bridge (This Package)│
│ - HTTP-like fetch wrapper │
│ - Event system (EventBridge) │
│ - React hooks and components │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ window.dioxusBridge (Injected) │
│ - Native Dioxus IPC channel │
│ - Injected by Rust before React │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Rust Backend (Dioxus + IPC Bridge)│
│ - Route handlers │
│ - Event emission │
│ - Business logic │
└─────────────────────────────────────┘
```
## Bundle Size
This package has **ZERO runtime dependencies** except for React peer dependencies.
- **RxJS removed**: Saved ~50-80KB (replaced with 2KB EventEmitter)
- **Minified**: ~8KB
- **Gzipped**: ~3KB
## Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
## Contributing
Contributions welcome! Please open an issue or PR.
## License
MIT © 2025 dioxus-react-bridge contributors
## Related Projects
- [Dioxus](https://github.com/DioxusLabs/dioxus) - Rust UI framework
- [dioxus-ipc-bridge](https://github.com/your-username/dioxus-ipc-bridge) - Rust side of IPC bridge
- [dioxus-react-integration](https://github.com/your-username/dioxus-react-integration) - React container component
## Support
- GitHub Issues: [Report bugs](https://github.com/your-username/dioxus-react-bridge/issues)
- Discussions: [Ask questions](https://github.com/your-username/dioxus-react-bridge/discussions)