How to Add Session Replay Without FullStory
To add session replay without FullStory, replace its snippet with an rrweb-based recorder that sends events to your own server. With Temps, set enableSessionRecording on TempsAnalyticsProvider: replay is off by default, masks all inputs and stores recordings in your own database.
Yes, you can add session replay without FullStory and keep every recording on your own server. Replace the FullStory snippet with a recorder built on the open-source rrweb library that sends events to an endpoint you control, either one you build yourself or Temps, which bundles session replay with web analytics, error tracking and deployments in one self-hosted install. You get DOM snapshots, incremental mutations, mouse, scroll and input tracking. What changes is where the data goes: Temps is free to self-host, so the only cost is the server you already pay for.
TL;DR: FullStory and Hotjar send DOM data and user interactions to the vendor's servers. You can record sessions with rrweb, an open-source library with about 20,000 GitHub stars (October 2026), and store them on your own infrastructure. Temps ships this as a built-in, opt-in feature: one provider prop turns it on, inputs are masked by default, and recordings stay in your own database. FullStory, for comparison, has a free plan up to 30,000 sessions a month and quote-only paid tiers, but no self-hosted option.
How Do I Add Session Replay to My App Without Using FullStory?
Add one provider to your React app, <TempsAnalyticsProvider enableSessionRecording={true} />, and recordings go to your own Temps server instead of FullStory's. Temps includes session replay, web analytics and error tracking as built-in features of its self-hosted deployment platform, with no FullStory account and no per-session pricing.
// React / Next.js
import { TempsAnalyticsProvider } from '@temps-sdk/react-analytics';
export default function RootLayout({ children }) {
return (
<html>
<body>
<TempsAnalyticsProvider
basePath="/api/_temps"
enableSessionRecording={true}
>
{children}
</TempsAnalyticsProvider>
</body>
</html>
);
}
Replay is off by default; enableSessionRecording turns it on, with maskAllInputs on by default. Events are batched, compressed and sent to your Temps instance. Outside React, the browser script tag takes data-session-recording="true", and there is a Svelte store; the session replay docs cover all three, plus masking, sampling and the CLI.
What Are the Alternatives to FullStory?
If you want a hosted tool or a broader comparison rather than a setup guide, our ranking of FullStory alternatives compares Temps, OpenReplay, PostHog, LogRocket, Hotjar and Microsoft Clarity on price, self-hosting and features. For open-source tools you run yourself, see the guide to self-hosted session replay.
Comparison: Where Your Recordings Go
| Temps | PostHog | Microsoft Clarity | FullStory | |
|---|---|---|---|---|
| Pricing | Free self-hosted / Cloud from $29/mo (coming soon) | Free cloud allowance (5,000 recordings/mo), then usage-based; hobby self-host free | Free, no traffic limits | Free plan up to 30,000 sessions/mo; paid tiers quote-only |
| Data location | Your server | PostHog cloud, or your server (hobby install) | Microsoft Azure | FullStory's US or EU data centre |
| Setup | enableSessionRecording prop (React) or data-session-recording script attribute | JS snippet | JS snippet | JS snippet |
| Includes deployments | Yes | No | No | No |
| Includes analytics | Yes (same install) | Yes | Heatmaps and dashboards | Yes (behavioural) |
| Includes error tracking | Yes (same install) | Yes | No | Partial |
| Includes uptime monitoring | Yes | No | No | No |
| Self-host option | One install on one server | Docker Compose "hobby" only; no new Kubernetes deployments | No | No |
| Recording retention | 15 days (fixed) | Plan-dependent | 30 days (favourites longer) | 12 months on the free plan |
Plans and prices verified on on each vendor's pricing page or docs.
PostHog's self-host limits come from its self-host docs; Clarity's from Microsoft's Clarity FAQ; FullStory's from its pricing page. See Temps vs FullStory and Temps vs PostHog for feature-level comparisons.
What Is Session Replay and Why Does It Matter?
Session replay reconstructs a user's experience by recording DOM mutations, mouse coordinates, scroll positions and input. It shows you exactly what users saw and did, which is why it is useful for debugging and UX work.
How Recording Works at a High Level
Session replay doesn't capture video. It's much lighter than that. The recording library takes an initial snapshot of the DOM, every element, attribute and text node, and serialises it into a JSON structure. From that point on, it watches for changes.
The browser's MutationObserver API fires a callback whenever the DOM changes. A new element appears? Recorded. Text content updates? Recorded. An attribute changes? Recorded. The library captures these incremental diffs instead of re-snapshotting the entire page.
Mouse movements are sampled at regular intervals (rrweb's default is every 50ms). Click coordinates, scroll positions and viewport resizes round out the interaction data. Some tools also capture console.log, console.error and network requests; Temps does not.
Why Teams Use Session Replay
Session replay answers questions that other tools can't:
- "Why did 12% of users abandon checkout on step 3?" Watch the sessions. Maybe a validation error is invisible below the fold.
- "The user says the button doesn't work." Pull up their session and see what happened.
- "Our error tracker shows 500 errors on /dashboard, but we can't reproduce it." Replay the session that triggered the error.
Bug reports from users are notoriously incomplete. Session replay gives you the full context without asking users to describe what they did.
When Do You Actually Need Session Replay?
Not every app needs it. Session replay adds a recorder script to your pages and generates storage costs. It's worth it in specific situations.
High checkout or conversion funnel abandonment. "12% of users drop off at step 3" is an analytics fact. Why they drop off requires watching them.
Vague bug reports. "It just doesn't work" tells you nothing. A session replay of that user shows you exactly what they clicked and what the page showed in response.
Onboarding confusion. Where do new users get stuck? Watching 20 onboarding sessions tells you more than most quantitative analyses.
Accessibility issues. Keyboard-only navigation problems, tab order issues and focus traps show up clearly in replays in ways that automated tests miss.
It is less useful for high-traffic content sites where most pages are informational and behaviour is predictable, and more useful for apps with complex workflows, multi-step forms or frequent "it's broken" reports.
What's the Privacy Problem with Third-Party Replay Tools?
Third-party session replay tools transmit page content, including anything rendered in the DOM, to the vendor's servers. That makes the vendor a processor of whatever personal data your pages display, and it is the part of replay that self-hosting changes.
Your DOM Contains More Than You Think
When a replay tool serialises the DOM, it captures everything visible on the page. That includes:
- Names and email addresses rendered in dashboards
- Partial card numbers displayed on confirmation screens
- Health data shown in patient portals
- Messages in chat interfaces
Even if you mask input fields, the rendered text on the page still gets recorded. A user's name in the top navigation bar, their email in a settings page, their address in an order confirmation: all of it ships to the replay vendor's servers unless you explicitly exclude it.
GDPR and CCPA Require Data Minimisation
GDPR's Article 5(1)(c) requires data minimisation: you should only process personal data that's necessary for a specific purpose. Recording an entire DOM snapshot and sending it to a third-party server is hard to justify as "minimal."
Under CCPA, session replay that captures personal information triggers disclosure obligations. You need to tell users you're recording their sessions and give California residents the right to opt out.
If the replay data never leaves your server, the transfer and processor questions go away and the data stays within your own jurisdiction. Minimisation, disclosure and consent still apply.
Consent Still Applies When You Self-Host
In the EU, recording a visitor's session generally needs prior consent under the ePrivacy rules, whether the recorder sends data to FullStory, Hotjar or your own server. Self-hosting changes who holds the data, not whether you need to ask. Temps also sets two first-party cookies for analytics (_temps_visitor_id and _temps_sid), which the data ownership and privacy docs say generally need consent in the EU.
The practical pattern is to record nothing until a visitor agrees. In Temps, set sessionSampleRate: 0 and call enableRecording() from the useSessionRecording() hook when the visitor accepts. Visitors who decline are not recorded, so your replays skew towards those who consent; plan your analysis with that in mind.
How Does Session Replay Work Under the Hood?
The rrweb library is the most widely used open-source session replay engine, with about 20,000 GitHub stars (October 2026), and forms the foundation for most self-hosted replay tools. Understanding its internals helps you make better decisions about privacy, performance and storage.
Step 1: Initial DOM Serialisation
When recording starts, the library walks the entire DOM tree. Every element gets a unique numeric ID. The serialiser captures tag names, attributes, text content and the tree structure. Stylesheets are inlined or referenced.
The result is a JSON object that represents the full page state at the moment recording began. Its size grows with the number of DOM nodes and inlined styles, so complex dashboards produce much larger snapshots than simple pages.
// Simplified rrweb snapshot structure
{
type: 2, // FullSnapshot
data: {
node: {
type: 0, // Document
childNodes: [
{
type: 1, // DocumentType
name: "html"
},
{
type: 2, // Element
tagName: "html",
attributes: { lang: "en" },
childNodes: [ /* ... recursive */ ]
}
]
}
},
timestamp: 1710000000000
}
Step 2: Incremental Mutation Recording
After the initial snapshot, MutationObserver takes over. Every DOM change produces an incremental event:
- Node additions: new elements added to the tree
- Node removals: elements removed from the tree
- Attribute changes: class toggles, style updates, data attributes
- Text changes: content updates in text nodes
These diffs are small. A typical interaction, such as clicking a button that opens a dropdown, produces a handful of small events, which is why replay is far more efficient than screen recording.
Step 3: User Interaction Capture
Mouse position is sampled at intervals and stored as coordinates with timestamps. Clicks record the target element ID and coordinates. Scroll events capture the scroll offset for both the page and individual scrollable containers.
Touch events on mobile work similarly: tap coordinates and scroll gestures are recorded as interaction events.
Step 4: Compression and Batching
Raw replay events pile up fast on interactive pages. Good implementations batch events into chunks, every few seconds or every N events, and compress them before sending. Repetitive JSON event streams compress very well with gzip or zlib, which keeps both network transfer and storage manageable. Temps, for example, flushes every 100 events or every 5 seconds by default and sends each batch zlib-compressed.
Step 5: Network and Console Capture (Optional)
rrweb plugins and some commercial tools also intercept:
console.log,console.warn,console.errorXMLHttpRequestandfetchcalls, with status codes and timing- Unhandled exceptions
This turns replay into more of a debugging tool, at the cost of capturing more data that may contain personal information. Temps does not do this: it records the DOM and user input only, and captures errors separately through its Sentry-compatible error tracking.
What Are the Open-Source Session Replay Options?
Several open-source projects offer session replay without third-party data transfer. Here's how the main building blocks compare.
rrweb: The Recording Engine
rrweb is a library, not a product. It gives you the recording and playback primitives: record() to capture events and rrweb-player to replay them. Everything else is your responsibility: transport, storage, search, playback UI and privacy masking.
Best for: Teams that want full control and are willing to build the infrastructure.
OpenReplay: Self-Hosted Replay Platform
OpenReplay is a full-stack session replay platform you can self-host, with a recording SDK, a backend processing pipeline and a web-based replay viewer. Its Docker deployment guide lists a minimum of 2 vCPUs, 8 GB of RAM and 50 GB of storage, and it runs several backing services.
Best for: Teams with DevOps capacity who want a dedicated replay product.
PostHog: Analytics Suite with Replay
PostHog bundles session replay into a broader product analytics platform. Self-hosting is now limited to a single-instance Docker Compose "hobby" deployment: PostHog's self-host docs say new Kubernetes deployments are no longer supported, recommend 4 vCPU and 16 GB of RAM, and suggest the cloud product above roughly 300k events or 1k recordings a month (October 2026).
Best for: Teams that want an analytics suite with replay and are happy to use PostHog Cloud for production volumes.
Comparison Table
| Feature | rrweb | OpenReplay | PostHog (self-hosted) |
|---|---|---|---|
| Type | Library | Full platform | Analytics suite |
| Minimum server | N/A (client-side) | 2 vCPU / 8 GB RAM / 50 GB | 4 vCPU / 16 GB RAM recommended |
| Storage backend | DIY | Managed by the platform | Managed by the platform |
| Privacy masking | maskAllInputs, rr-mask / rr-block classes | Built-in rules | Built-in rules |
| Playback UI | Basic player | Full dashboard | Full dashboard |
| Setup complexity | Build everything | Docker Compose | Docker Compose (hobby only) |
| Production self-hosting | Whatever you build | Yes | No (cloud recommended above hobby volumes) |
OpenReplay and PostHog are powerful, but neither is lightweight to self-host, and PostHog no longer offers a production self-hosted path. That is the gap a single bundled install fills.
How Do You Build a Minimal Session Replay System?
A basic session replay setup with rrweb takes surprisingly little code: a few dozen lines on the client and on the server. The hard parts are storage management and PII handling, not the recording logic itself.
Client-Side: Recording with rrweb
Install rrweb and start recording:
npm install rrweb
import { record } from 'rrweb';
const events: any[] = [];
let stopRecording: (() => void) | undefined;
// Start recording
stopRecording = record({
emit(event) {
events.push(event);
},
// Mask all input fields by default
maskAllInputs: true,
// Block elements with this class from being recorded
blockClass: 'rr-block',
// Sample mouse movements every 50ms (default)
sampling: {
mousemove: 50,
scroll: 150,
},
});
// Batch send every 10 seconds
setInterval(() => {
if (events.length === 0) return;
const batch = events.splice(0, events.length);
fetch('/api/replay/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sessionId: getSessionId(),
events: batch,
}),
// Use keepalive for tab close / navigation
keepalive: true,
});
}, 10_000);
Server-Side: Receiving and Storing Events
A minimal Express endpoint to receive and store replay data:
import express from 'express';
import { writeFile, mkdir } from 'fs/promises';
import { gzipSync } from 'zlib';
const app = express();
app.use(express.json({ limit: '5mb' }));
app.post('/api/replay/events', async (req, res) => {
const { sessionId, events } = req.body;
if (!sessionId || !Array.isArray(events)) {
return res.status(400).json({ error: 'Invalid payload' });
}
const dir = `./replay-data/${sessionId}`;
await mkdir(dir, { recursive: true });
const compressed = gzipSync(JSON.stringify(events));
const filename = `${dir}/${Date.now()}.json.gz`;
await writeFile(filename, compressed);
res.status(204).end();
});
Playback: Reconstructing the Session
rrweb provides rrweb-player for replay:
import rrwebPlayer from 'rrweb-player';
import 'rrweb-player/dist/style.css';
// Load all event batches for a session
const events = await loadSessionEvents(sessionId);
new rrwebPlayer({
target: document.getElementById('player-container')!,
props: {
events,
width: 1280,
height: 720,
autoPlay: true,
showController: true,
},
});
The Gotchas You'll Hit
Building this yourself sounds straightforward, but several problems surface quickly:
- Storage grows fast. Every recorded session adds compressed event files, so you need retention policies and cleanup jobs from day one (the storage math is below).
keepalivehas a size limit. Browsers cap in-flightkeepaliverequest bodies at 64 KB, so large batches sent on page unload fail. Flush often enough that the final batch stays small. If your endpoint is on a different subdomain, you also need CORS headers.- Iframe content won't record. Cross-origin iframes are opaque to the recorder. If your app embeds third-party widgets, those sections appear as blank rectangles in replay.
- CSS-in-JS breaks styling. Libraries like styled-components inject styles into
<style>tags at runtime. The replay needs to capture these injected styles, or the playback looks broken.
These problems are solvable, but each one adds engineering time. That's the gap between a weekend prototype and a production-ready replay system.
What Should You Mask for Privacy-First Recording?
Even with self-hosted replay, you still need to mask sensitive content. Princeton researchers found in 2017 that session replay scripts on popular websites captured sensitive data such as card details and passwords. The lesson holds regardless of where the data is stored.
Input Fields: The Obvious Target
rrweb's maskAllInputs: true option replaces all input values with asterisks during recording. This catches:
- Password fields
- Email inputs
- Card numbers
- Phone numbers
- Search queries
Always enable this by default (Temps does). Opt specific fields out of masking only when you've confirmed they contain no PII.
Rendered Text: The Less Obvious Target
Input masking isn't enough. Think about what's rendered as plain text on your pages:
- User names in headers and navigation bars
- Email addresses on profile pages
- Billing addresses on order confirmations
- Account numbers in financial dashboards
- Health data in medical applications
For these, mask the text of whole containers. rrweb's default class for this is rr-mask; in Temps you can also use a maskTextSelector (default [data-mask]):
<!-- Mask the entire user profile section -->
<div class="rr-mask">
<h2>John Doe</h2>
<p>john@example.com</p>
<p>123 Main St, Springfield</p>
</div>
Blocking Entire Sections
Some page regions shouldn't be recorded at all, not even as masked content. The rr-block class (rrweb's default blockClass) replaces an element with an empty placeholder:
<!-- Block the payment form entirely -->
<form class="rr-block payment-form">
<!-- Nothing in here gets serialised -->
</form>
The difference matters: masked elements show asterisks (the viewer can see something was there), while blocked elements are replaced by a blank placeholder.
A Practical Masking Strategy
Here's a sensible starting point:
- Enable
maskAllInputsglobally, with no exceptions by default - Mask any component that renders user-specific data (profiles, settings, account info)
- Block payment forms, medical records and legal documents
- Exclude sensitive routes entirely; in Temps,
excludedPathsskips recording on matching paths such as/account/payment - Audit your routes: walk through every page as a logged-in user and check what PII is visible
- Test the replay: record a session, play it back, and check that nothing sensitive is visible
Most teams mask input fields and consider the job done. But the bigger risk is rendered text, data that's already in the DOM as plain HTML: a user's name in the sidebar, their email in a toast notification, their address on a shipping page. DOM serialisation captures all of these even though nobody typed them.
How Does Session Replay Performance Affect Your App?
Session replay adds overhead, but usually less than you'd expect. The main costs are the initial snapshot of large DOM trees, the recorder script itself and the upload bandwidth.
CPU and Memory Impact
The initial DOM serialisation is the most expensive operation, and it scales with page size: a dashboard with thousands of nodes takes noticeably longer to snapshot than a simple page. After that, mutation callbacks are lightweight.
Memory use depends on how you buffer events. Flushing every few seconds keeps the buffer small; it matters most on low-end mobile devices.
Network Overhead
Quiet pages send small batches; heavily interactive dashboards send much larger ones. Measure your own pages before rolling out to everyone.
The difference with self-hosted replay is that the data goes to your own domain, with no extra DNS lookup or TLS handshake to a third-party host.
When to Disable Recording
Not every session needs recording. Sampling reduces overhead and storage:
// Record 10% of sessions
const shouldRecord = Math.random() < 0.1;
if (shouldRecord) {
record({ emit(event) { /* ... */ } });
}
In Temps the same thing is one option, sessionSampleRate: 0.1. You can also record selectively, for example only on specific pages or only after consent, which cuts storage while keeping the sessions that matter most.
How Does Temps Handle Session Replay?
Temps includes session replay as a built-in platform feature: no separate replay service to deploy and no extra database cluster. Recording, storage and playback run in the same install that handles your deployments, analytics and error tracking.
Temps is one self-hosted platform that replaces seven SaaS tools: Vercel (deployments), PostHog (analytics and session replay), Sentry (error tracking), Pingdom (uptime monitoring), Datadog (OpenTelemetry traces, metrics and logs), Resend (transactional email) and E2B (AI agent sandboxes). For replay, that means no separate service beyond the server you already run.
Session replay, web analytics, error tracking and deployment hosting all need the same underlying pieces: a server, a database and an HTTP endpoint. Running them as four separate SaaS tools means paying for that infrastructure four times.
Setup: One Prop
Use the provider shown at the top of this guide with enableSessionRecording={true}. To control what is recorded, pass a sessionRecordingConfig:
<TempsAnalyticsProvider
enableSessionRecording={true}
sessionRecordingConfig={{
sessionSampleRate: 0.1, // record 10% of sessions
maskAllInputs: true, // the default
excludedPaths: ['/admin/*', '/account/payment'],
}}
>
{children}
</TempsAnalyticsProvider>
Every option is listed in the session replay configuration reference.
What's Different from DIY
Building on rrweb yourself works for a proof of concept. Temps handles the parts that take weeks to build properly:
- Storage management: batched, compressed ingest and automatic cleanup after 15 days
- Playback UI: a session player in the Temps dashboard with variable-speed playback, a timeline with click and navigation markers, and per-visitor session history
- Error context: errors from the Sentry-compatible error tracker are stored with the session ID, so you can go from an error to the session it happened in
- Privacy controls:
maskAllInputson by default, plus text masking, element blocking and path exclusion in the recorder config - Sampling:
sessionSampleRateper app, including0to record nobody until consent - CLI access: list, export and delete sessions with
bunx @temps-sdk/cli session-replay
Data Stays on Your Server
This is the fundamental difference from FullStory, Hotjar and Microsoft Clarity. Every session replay event is stored in your Temps instance's TimescaleDB (PostgreSQL) database and never passes through a third-party server. If your server is in Frankfurt, your replay data is in Frankfurt.
For teams under GDPR, that removes the international transfer question: no Standard Contractual Clauses and no DPA with a replay vendor. Consent and masking still apply, as covered above.
Temps session replay is built on rrweb, the same open-source library used by PostHog and OpenReplay. It records the DOM and user input only, with no console, network or performance capture.
Pricing
Temps is free to self-host (MIT / Apache 2.0), with no per-seat fees, no per-session pricing and no bandwidth bills; the only real cost is the server you run it on. Temps Cloud, a hosted control plane for your own servers, is coming soon from $29/month.
What Does Self-Hosted Session Replay Actually Cost in Storage?
If you're self-hosting, the storage math matters. Assume about 200KB per compressed session, then measure your own: 10,000 sessions a month is about 2GB, and 100,000 sessions a month is about 20GB. If you build your own pipeline and keep a year of sessions at that volume, that is roughly 240GB, about $5-6 a month at typical object-storage prices. Temps keeps replays for 15 days, so its storage stays at roughly half a month's worth of sessions.
For most teams, storage for self-hosted replay is small next to SaaS pricing. The main operational cost is running and maintaining the replay infrastructure. Tools like OpenReplay or Temps take that on for you; raw rrweb leaves it to you.
The question to ask: does the debugging and UX value of replay justify the cost (money, storage, operational overhead and privacy review) at your current traffic level? For complex products, usually yes. For simple sites, probably not.
Frequently Asked Questions
How do I add session replay to my app without using FullStory?
Swap the FullStory snippet for a recorder that sends events to your own server. The fastest route is Temps: add <TempsAnalyticsProvider enableSessionRecording={true} /> to a React or Next.js app (or data-session-recording="true" on the script tag), and rrweb-based recordings are stored in your own Temps database, with inputs masked by default, alongside web analytics, error tracking and deployments. If you'd rather not adopt a platform, install rrweb directly and write your own capture endpoint: more code, but full control over storage and retention. Either way, you get DOM snapshots and mouse, scroll and input tracking without sending recordings to a third party.
Do I need consent to record sessions on my own server?
Generally yes, in the EU. Self-hosting removes the third-party transfer, but recording a visitor's session still needs prior consent under the ePrivacy rules, and Temps' two first-party analytics cookies (_temps_visitor_id and _temps_sid) generally need consent too. In Temps, set sessionSampleRate: 0 and call enableRecording() once the visitor accepts. For the wider picture, see our guide to privacy-first analytics and session replay.
Can I self-host a session replay tool like FullStory?
Yes. Session replay is one of the easier observability tools to self-host because the recording engine, rrweb, is open source and runs in the browser; only the storage and playback backend needs a server. You can build your own pipeline on rrweb or use a platform that bundles it. Temps includes replay in its self-hosted install and stores recordings in your own database. OpenReplay also self-hosts (minimum 2 vCPU and 8 GB of RAM); PostHog's self-hosted option is now a hobby-scale Docker Compose install. Our guide to self-hosted session replay compares the options.
How much storage does session replay require?
It depends on page complexity and session length, so measure a sample of your own sessions. As a planning figure, at about 200KB per compressed session, 1,000 sessions a day is roughly 6GB a month; sampling 10% of sessions cuts that to well under 1GB. Temps deletes replays after 15 days, so stored volume stays at about half a month of sessions.
Does session replay slow down my website?
Usually not noticeably. DOM-based recorders take one snapshot on page load and then stream small mutation and input events in batches. The costs are that initial snapshot (larger on complex pages), the recorder script and the upload bandwidth. Recording fewer sessions with a sample rate below 1.0 reduces all three.
Can I use session replay without violating GDPR?
Yes, with the right setup. Keep recordings on your own infrastructure, mask inputs and sensitive rendered text, block payment and health sections, ask for consent before recording, disclose replay in your privacy policy, and be able to delete a person's sessions. Temps masks inputs by default, keeps replays for 15 days and lets you delete sessions from the CLI or dashboard.
How does self-hosted session replay compare to FullStory's features?
FullStory adds frustration detection (rage clicks, dead clicks), searchable session metadata, heatmaps and AI insights. A self-hosted rrweb setup gives you recording, playback and privacy masking, the core you need for most debugging, without sending user data to a third party. For a side-by-side of price and features, see our comparison of FullStory alternatives.
Keep Your Users' Sessions on Your Own Server
Session replay is too useful to skip, but it doesn't require shipping DOM data to a third-party server. The open-source tooling, particularly rrweb, is mature enough to build on, and the self-hosted options keep improving.
If you want replay without building the infrastructure, the simplest path is a platform that bundles it with your deployment pipeline: record sessions, play them back in the same dashboard where you manage deployments, and keep the data on your own server.
Temps includes session replay, web analytics, error tracking and deployments in one self-hosted install. Install it, enable replay with one prop, and you're recording, with no FullStory contract and no data leaving your infrastructure.
curl -fsSL https://temps.sh/deploy.sh | bash
Then follow the session replay docs to turn recording on.
Get weekly updates