React
Set up Apperio in a React single-page app, with an error boundary.
This guide is for a React app that runs in the browser, such as one built with Vite. For Next.js, use the Next.js guide. The code works with React 18 and 19. Install the SDK first: npm install apperio.
1. Create the logger
Create one logger in its own module and import it wherever you need it:
import { Apperio } from 'apperio';
export const logger = new Apperio({
apiKey: 'your-api-key',
projectId: 'your-project-id',
environment: 'production',
serviceName: 'web',
release: '1.0.0',
});Fill these in from your build’s environment variables. In Vite, for example, read import.meta.env.VITE_APPERIO_API_KEY and import.meta.env.MODE. The API key is public once it is in your bundle; that is expected.
2. Load it before rendering
Import the module at the top of your entry file, so the logger exists before React renders anything and catches errors from the very first render:
import './apperio';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
import { ErrorBoundary } from './ErrorBoundary';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<ErrorBoundary>
<App />
</ErrorBoundary>
</StrictMode>,
);Because the logger is created once at module level, Strict Mode’s double rendering doesn’t create a second one.
3. Report errors React catches
When an error boundary catches a rendering error, React shows the fallback and the error doesn’t reach the window, so the SDK never sees it. Log it from componentDidCatch:
import { Component, type ErrorInfo, type ReactNode } from 'react';
import { logger } from './apperio';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError(): State {
return { hasError: true };
}
componentDidCatch(error: Error, info: ErrorInfo) {
logger.error('React render error', error, {
componentStack: info.componentStack ?? undefined,
});
}
render() {
if (this.state.hasError) {
return <p>Something went wrong. Please reload the page.</p>;
}
return this.props.children;
}
}The component stack goes with the error, so you can see which component failed. Errors outside rendering, such as in event handlers and timers, don’t go through boundaries; the SDK captures those itself.
4. Log from components
import { logger } from './apperio';
export function CheckoutButton({ orderId }: { orderId: string }) {
const handleClick = async () => {
logger.info('Checkout started', { orderId });
try {
await processOrder(orderId);
} catch (err) {
logger.error('Checkout failed', err instanceof Error ? err : new Error(String(err)), {
orderId,
});
}
};
return <button onClick={handleClick}>Pay now</button>;
}To tag every later log with the signed-in user, call logger.setContext({ userId }) after sign-in and logger.clearContext() after sign-out. Avoid emails and names: an ID is enough to find the user in your own systems.
Notes
- Route changes made with React Router, or anything else that uses the History API, are logged as page views and stay in the same session.
- Turn on session replay from the dashboard; no code change is needed.
Seeing it work
environment set to production just for the test, and it appears in Issues within a few seconds.