Overview
Getting started
Silent error tracker for frontend applications.
Surfacer captures the errors that don't crash your app but silently ruin your users' experience unhandled promise rejections, silent network failures, and console.error calls that disappear into the void.
| Type | Example |
|---|---|
| Unhandled promise rejections | Promise.reject() without .catch() |
| Uncaught JS errors | throw new Error() without try/catch |
| console.error calls | Errors swallowed in catch blocks |
| Fetch HTTP failures | fetch() response with status outside 2xx |
| XHR HTTP failures | XMLHttpRequest response with status outside 2xx |
| console.warn calls (optional) | Warnings when captureWarnings is enabled |
Get your API key from your Surfacer dashboard, then initialize the SDK once in your app's entry point.
import { init } from 'surfacer';init('your-api-key');That's it. Surfacer is now listening for silent errors.
To also capture console.warn calls:
import { init } from 'surfacer';init('your-api-key', true);Create a free account at surfacer.dev to:
- Create a project and get your API key
- View captured errors grouped by type
- See stack traces, filenames, line numbers and occurrence counts
- Mark errors as resolved or ignored
Surfacer is intentionally lightweight under 2kb gzipped. No dependencies.
License: MIT.
Get your API key →Setup
Installation
Install Surfacer with your preferred package manager.
npm install surfacer# oryarn add surfacer# orpnpm add surfacerFramework integration
React
In src/main.tsx, before createRoot:
$ npm install surfacerIn src/main.tsx, before createRoot:
import { init } from 'surfacer';init('your-api-key');const root = createRoot(document.getElementById('root')!);root.render(<App />);Framework integration
Next.js
Next.js (App Router): create a SurfacerProvider component, then add it to your root layout.tsx.
$ npm install surfacer// components/SurfacerProvider.tsx
'use client';import { useEffect } from 'react';import { init } from 'surfacer';export default function SurfacerProvider() { useEffect(() => { init('your-api-key'); }, []); return null;}// app/layout.tsximport SurfacerProvider from '@/components/SurfacerProvider';export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang='en'> <body> <SurfacerProvider /> {children} </body> </html> );}Framework integration
Vue
Vue 3 in src/main.ts:
$ npm install surfacerIn src/main.ts:
import { createApp } from 'vue';import { init } from 'surfacer';import App from './App.vue';init('your-api-key');createApp(App).mount('#app');Framework integration
Nuxt
Nuxt 3: create a plugin in plugins/surfacer.client.ts:
$ npm install surfacerCreate a plugin in plugins/surfacer.client.ts:
import { init } from 'surfacer';export default defineNuxtPlugin(() => { init('your-api-key');});Framework integration
Svelte
Svelte / SvelteKit in src/routes/+layout.svelte:
$ npm install surfacerIn src/routes/+layout.svelte:
<script> import { onMount } from 'svelte'; import { init } from 'surfacer'; onMount(() => { init('your-api-key'); });</script><slot />Framework integration
Angular
In src/main.ts:
$ npm install surfacerIn src/main.ts:
import { init } from 'surfacer';import { bootstrapApplication } from '@angular/platform-browser';import { AppComponent } from './app/app.component';init('your-api-key');bootstrapApplication(AppComponent);Framework integration
Symfony (Stimulus)
Surfacer works natively with Stimulus controllers.
$ npm install surfacerIn assets/app.js:
import { init } from 'surfacer';init('your-api-key');No additional configuration is needed.
Framework integration
Alpine.js
Surfacer works natively with Alpine.js.
$ npm install surfacerInitialize Surfacer before Alpine.start():
import Alpine from 'alpinejs';import { init } from 'surfacer';init('your-api-key');window.Alpine = Alpine;Alpine.start();No additional configuration is needed.
Framework integration
Vanilla JS
Vanilla JS / TypeScript:
$ npm install surfacerimport { init } from 'surfacer';init('your-api-key');Runtime
How it works
- Call init() once in your app's entry point
- Surfacer registers global listeners on window and patches console.error
- Surfacer also patches fetch and XMLHttpRequest to capture failed HTTP requests
- Errors are queued locally and sent in batches every 10 seconds
- If your network is down, errors are retried automatically on the next batch
- Identical errors are deduplicated using fingerprinting — you see occurrence counts, not noise