npm # @awell-health/navi-react
React components and hooks for integrating Navi care flows

What is this package?
This is the React SDK for Navi care flows. Instead of using the iframe-based loader script, this package provides native React components and hooks that integrate seamlessly with your React application.
Key features:
- ⚛️ Native React integration - Components, hooks, and TypeScript support
- 🔧 Declarative API - Use JSX components instead of imperative JavaScript
- 🎣 React Hooks -
useFlowEmbed for programmatic control
- 📱 SSR Compatible - Works with Next.js, Remix, and other React frameworks
- 🔒 Type Safe - Full TypeScript definitions included
- 🎯 15KB bundle size - Optimized for performance
When to use this package?
Choose @awell-health/navi-react when:
- ✅ You're building a React/Next.js application
- ✅ You want native React components instead of iframes
- ✅ You need tight integration with React state/lifecycle
- ✅ You want TypeScript support out of the box
Use @awell-health/navi (the loader script) when:
- ❌ You're not using React
- ❌ You want the simplest possible integration
- ❌ You need to embed flows in existing non-React pages
Installation
npm install @awell-health/navi-react
yarn add @awell-health/navi-react
pnpm add @awell-health/navi-react
Quick Start
1. Wrap your app with NaviProvider
import { NaviProvider } from "@awell-health/navi-react";
function App() {
return (
<NaviProvider publishableKey="pk_test_your_key_here">
<YourApp />
</NaviProvider>
);
}
2. Use the FlowEmbed component
import { FlowEmbed } from "@awell-health/navi-react";
function OnboardingPage() {
return (
<div>
<h1>Complete Your Health Assessment</h1>
<FlowEmbed
flowId="health_assessment_123"
onFlowCompleted={(data) => {
console.log("Assessment completed!", data);
// Redirect or update UI
}}
/>
</div>
);
}
Complete Example
import React, { useState } from "react";
import {
NaviProvider,
FlowEmbed,
useFlowEmbed,
} from "@awell-health/navi-react";
function HealthScreening() {
const [isCompleted, setIsCompleted] = useState(false);
const handleActivityCompleted = (data) => {
console.log("Activity completed:", data.activityId);
};
const handleFlowCompleted = (data) => {
console.log("Screening completed!");
setIsCompleted(true);
};
const handleError = (error) => {
console.error("Screening error:", error);
alert("Something went wrong. Please try again.");
};
if (isCompleted) {
return <div>✅ Health screening completed!</div>;
}
return (
<FlowEmbed
flowId="health_screening_456"
className="my-flow-styles"
options={{
context: {
patientId: "patient_123",
source: "react-app",
},
}}
onActivityCompleted={handleActivityCompleted}
onFlowCompleted={handleFlowCompleted}
onError={handleError}
/>
);
}
function DynamicFlow() {
const [flowId, setFlowId] = useState("");
const { embed, destroy, isEmbedded, error } = useFlowEmbed();
const handleEmbed = () => {
embed(flowId, "#dynamic-container", {
context: { timestamp: Date.now() },
});
};
return (
<div>
<input
value={flowId}
onChange={(e) => setFlowId(e.target.value)}
placeholder="Enter flow ID"
/>
<button onClick={handleEmbed} disabled={!flowId}>
Embed Flow
</button>
<button onClick={destroy} disabled={!isEmbedded}>
Remove Flow
</button>
{error && <div>Error: {error.message}</div>}
<div id="dynamic-container" style={{ minHeight: 300 }} />
</div>
);
}
function App() {
return (
<NaviProvider
publishableKey="pk_test_your_key_here"
debug={true} // Enable debug mode in development
>
<div className="app">
<h1>My Healthcare App</h1>
<HealthScreening />
<DynamicFlow />
</div>
</NaviProvider>
);
}
export default App;
API Reference
<NaviProvider>
Provides Navi context to all child components. Must wrap any components using Navi.
<NaviProvider
publishableKey="pk_test_your_key"
apiUrl="https://api.navi.awell.com"
debug={false}
>
<App />
</NaviProvider>
Props:
publishableKey (string, required) - Your Navi publishable key
apiUrl (string, optional) - Custom API URL (for testing)
debug (boolean, optional) - Enable debug logging
children (ReactNode, required) - Your app components
<FlowEmbed>
Renders a care flow as a React component.
<FlowEmbed
flowId="flow_123"
className="my-styles"
options={{ context: { userId: "123" } }}
onActivityLoaded={(data) => {}}
onActivityCompleted={(data) => {}}
onFlowCompleted={(data) => {}}
onError={(error) => {}}
/>
Props:
flowId (string, required) - The care flow ID to render
className (string, optional) - CSS class for styling
options (object, optional) - Flow configuration
context (object) - Additional context data
onActivityLoaded (function, optional) - Called when activity loads
onActivityCompleted (function, optional) - Called when activity completes
onFlowCompleted (function, optional) - Called when entire flow completes
onError (function, optional) - Called when errors occur
useNavi()
Hook to access Navi context and loading state.
const { config, isLoaded, error } = useNavi();
Returns:
config - The Navi configuration (publishableKey, apiUrl, debug)
isLoaded - Boolean indicating if Navi script is loaded
error - Any loading errors
useFlowEmbed()
Hook for programmatic flow embedding and management.
const { embed, destroy, isEmbedded, error } = useFlowEmbed();
Returns:
embed(flowId, container, options) - Function to embed a flow
destroy() - Function to remove the embedded flow
isEmbedded - Boolean indicating if a flow is currently embedded
error - Any embedding errors
Integration Patterns
Next.js App Router
import { NaviProvider } from '@awell-health/navi-react';
export default function RootLayout({ children }) {
return (
<html>
<body>
<NaviProvider publishableKey={process.env.NEXT_PUBLIC_NAVI_KEY}>
{children}
</NaviProvider>
</body>
</html>
);
}
import { FlowEmbed } from '@awell-health/navi-react';
export default function OnboardingPage() {
return (
<div>
<h1>Welcome!</h1>
<FlowEmbed flowId="onboarding_flow" />
</div>
);
}
Conditional Rendering
function PatientDashboard({ patient }) {
const showHealthCheck = patient.needsHealthCheck;
return (
<div>
<h1>Dashboard</h1>
{showHealthCheck && (
<FlowEmbed
flowId="daily_health_check"
options={{ context: { patientId: patient.id } }}
onFlowCompleted={() => {
// Refresh patient data
mutate(`/api/patients/${patient.id}`);
}}
/>
)}
{/* Rest of dashboard */}
</div>
);
}
Error Handling
function RobustFlow() {
const [hasError, setHasError] = useState(false);
if (hasError) {
return (
<div className="error-state">
<p>Unable to load care flow.</p>
<button onClick={() => setHasError(false)}>Try Again</button>
</div>
);
}
return (
<FlowEmbed
flowId="sensitive_flow"
onError={(error) => {
console.error("Flow error:", error);
setHasError(true);
}}
/>
);
}
TypeScript Usage
import {
NaviProvider,
FlowEmbed,
FlowEmbedProps,
useNavi,
} from "@awell-health/navi-react";
interface CustomFlowProps {
patientId: string;
flowType: "intake" | "followup" | "discharge";
}
const CustomFlow: React.FC<CustomFlowProps> = ({ patientId, flowType }) => {
const { isLoaded } = useNavi();
const handleCompleted = (data: { flowId: string; completedAt: string }) => {
console.log("Flow completed:", data);
};
if (!isLoaded) {
return <div>Loading Navi...</div>;
}
return (
<FlowEmbed
flowId={`${flowType}_flow`}
options={{
context: { patientId },
}}
onFlowCompleted={handleCompleted}
/>
);
};
Styling
The components can be styled with CSS:
.navi-flow-container {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
}
.navi-loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
color: #666;
}
.navi-error {
background: #fee;
border: 1px solid #fcc;
padding: 15px;
border-radius: 4px;
color: #c00;
}
Performance
- Bundle size: ~15KB gzipped
- Code splitting: Import only what you need
- SSR compatible: Works with server-side rendering
- Lazy loading: Flow content loads on demand
Browser Support
- React 16.8+ (hooks required)
- Modern browsers (same as Navi loader)
Migration from Loader Script
If you're migrating from @awell-health/navi:
const navi = Navi("pk_test_key");
navi.renderFlow("flow_123", "#container");
<NaviProvider publishableKey="pk_test_key">
<FlowEmbed flowId="flow_123" />
</NaviProvider>;
Support
License
MIT