React hooks and components for seamless IPC communication with Dioxus Rust backend.
- ✅ 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
# 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-domImport global type definitions in your entry file for full TypeScript support of Dioxus-injected APIs:
// src/index.tsx or src/main.tsx
import '@deckyfx/dioxus-react-bridge/global';This provides complete type definitions for:
window.dioxusBridge- Main IPC bridgewindow.ipc- Low-level IPC channelwindow.interpreter- Dioxus virtual DOM interpreterwindow.showDXToast(),window.scheduleDXToast(),window.closeDXToast()- Toast notifications
Alternative: Add to tsconfig.json
{
"compilerOptions": {
"types": ["@deckyfx/dioxus-react-bridge/global"]
}
}import { IPCReady, RustIPCProvider } from '@deckyfx/dioxus-react-bridge';
import '@deckyfx/dioxus-react-bridge/global'; // TypeScript types
function App() {
return (
<IPCReady>
<RustIPCProvider>
<MyApp />
</RustIPCProvider>
</IPCReady>
);
}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>
);
}import { useRustEventListener } from '@deckyfx/dioxus-react-bridge';
function HeartbeatMonitor() {
const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');
return <div>Heartbeat: {heartbeat?.count || 'Waiting...'}</div>;
}// 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);
}dx new my-app
cd my-appAdd to Cargo.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"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"
}
}
}# 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 typescriptCreate src/index.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>
);
}Create build.ts using the built-in build utility:
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:
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:
bun run build.tsDevelopment Watch Mode:
Create watch.ts for automatic rebuilding during development:
import { watchForDioxus } from '@deckyfx/dioxus-react-bridge/build';
await watchForDioxus({
dioxusAssetsPath: '../my-dioxus-app/assets/react',
verbose: true
});Run watch mode:
bun run watch.tsThe build utility automatically copies the bundle to your Dioxus assets directory (if dioxusAssetsPath is specified).
If you didn't use dioxusAssetsPath, manually copy:
cp react-app/dist/bundle.js ../my-app/assets/react/bundle.jscd my-app
dx serveThe package includes a configurable build utility to simplify bundling React apps for Dioxus.
Main build function that handles Tailwind CSS processing, bundling, and copying to Dioxus assets.
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)
Watch mode for automatic rebuilding during development.
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_modulesand processed files - Initial build on start
Create a reusable build configuration with defaults applied.
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);Suspense-like component that waits for window.dioxusBridge to be initialized.
<IPCReady
fallback={<Spinner />}
onReady={() => console.log('IPC ready!')}
timeout={10000}
>
<App />
</IPCReady>Props:
fallback?: ReactNode- Custom loading UIonReady?: () => void- Callback when IPC is readytimeout?: number- Timeout in ms (default: 10000)
Context provider for IPC functionality.
<RustIPCProvider>
<App />
</RustIPCProvider>Main hook for HTTP-like IPC requests.
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 functionisReady: boolean- Whether IPC is ready
Listen to events from Rust.
const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');Parameters:
channel: string- Event channel name
Returns:
T | null- Latest event data
Emit events to a channel.
const emit = useRustEventEmitter<{ message: string }>('notification');
emit({ message: 'Hello!' });Parameters:
channel: string- Event channel name
Returns:
(data: T) => void- Emit function
Manage long-running streaming tasks with progress tracking.
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) => voidonChunk?: (chunk) => voidonComplete?: (result) => voidonError?: (error) => voidautoReassemble?: boolean
Returns:
taskId: string | nullisRunning: booleanprogress: StreamingProgress | nullchunks: StreamingChunk[]result: T | nullerror: Error | nullstart: (url, options) => Promise<void>cancel: () => voidreset: () => void
import type {
IpcFetchOptions,
IpcResponse,
IpcErrorResponse,
StreamingProgress,
StreamingChunk,
StreamingTaskOptions,
StreamingTaskState,
DioxusBridge,
IPCBridgeInstance,
} from '@deckyfx/dioxus-react-bridge';Import global type definitions for complete TypeScript support of Dioxus-injected APIs:
// 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 APIfetch<T>(url, options?)- Make IPC requestsrustEmit(channel, data)- Emit events to React (called by Rust)IPCBridge- Event system for pub/subipc.send(data)- Low-level sendipc.hasIPCBridge()- Check for event system
-
window.ipc- Low-level IPC channelpostMessage(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
- DOM manipulation:
-
window.showDXToast()- Show toast notification -
window.scheduleDXToast()- Schedule toast after reload -
window.closeDXToast()- Close current toast
Example with Full Type Safety:
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)
});
}┌─────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────┘
This package has ZERO runtime dependencies except for React peer dependencies.
- RxJS removed: Saved ~50-80KB (replaced with 2KB EventEmitter)
- Minified: ~8KB
- Gzipped: ~3KB
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
Contributions welcome! Please open an issue or PR.
MIT © 2025 dioxus-react-bridge contributors
- Dioxus - Rust UI framework
- dioxus-ipc-bridge - Rust side of IPC bridge
- dioxus-react-integration - React container component
- GitHub Issues: Report bugs
- Discussions: Ask questions