> ## Documentation Index
> Fetch the complete documentation index at: https://subtext.fullstory.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Subtext Capture Snippet: What It Is and How It Works

> The Subtext capture snippet loads fs.js from Fullstory's edge network and records full production sessions with automatic form input masking.

The Subtext capture snippet is a short piece of JavaScript you add to your application's HTML `<head>`. When a user visits a page that carries it, the snippet bootstraps an async loader, fetches `fs.js` from Fullstory's edge network, and begins recording the session — all without blocking the page render. Every click, scroll, navigation, network request, and console error flows into a structured event stream that your AI coding agent can query through Subtext's MCP tools. Because the snippet is async and lightweight, you can run it on every production session without any meaningful performance cost.

<Note>
  Subtext is built on Fullstory's capture infrastructure. The snippet points at `edge.fullstory.com/s/fs.js` — the same delivery network behind Fullstory's enterprise session replay. You get that reliability and global reach without needing a separate Fullstory account; Subtext manages the integration for you.
</Note>

## What the snippet records

Once loaded, the snippet captures:

* **DOM mutations and layout** — the full visual state of every page, reconstructed accurately in replay
* **User interactions** — clicks, taps, keyboard events, scrolls, focus changes, and form submissions
* **Navigation** — page loads, client-side route changes (including SPAs), and redirects
* **Network activity** — XHR and Fetch requests with timing and status codes
* **Console output** — errors, warnings, and log messages correlated to the timeline
* **JavaScript exceptions** — uncaught errors with stack traces, timestamped against the session

Form inputs — `<input>`, `<textarea>`, `<select>` — are **masked from first install**. The text your users type never leaves the browser. You can extend masking to other elements using CSS selectors after install; Subtext's privacy tools let your agent propose and promote those rules without manual configuration.

## What the snippet looks like

Every Subtext snippet follows the same structure. Your organisation ID and namespace are baked in at install time; the loader itself is boilerplate that stays identical across all projects.

```html theme={null}
<script>
  window['_fs_host'] = 'fullstory.com';
  window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
  window['_fs_org'] = 'YOUR_ORG_ID';
  window['_fs_namespace'] = 'FS';
  (function(m,n,e,t,l,o,g,y){
    if (e in m) { if(m.console && m.console.log) {
      m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');
    } return; }
    g=m[e]=function(a,b,s){ g.q? g.q.push([a,b,s]) : g._api(a,b,s); };
    g.q=[];
    o=n.createElement(t); o.async=1; o.crossOrigin='anonymous';
    o.src='https://'+m._fs_script;
    y=n.getElementsByTagName(t)[0];
    y.parentNode.insertBefore(o,y);
    g.identify=function(i,v,s){ g(l,{uid:i},s); if(v) g(l,v,s); };
    g.setUserVars=function(v,s){ g(l,v,s); };
    g.event=function(i,v,s){ g('event',{n:i,p:v},s); };
    g.anonymize=function(){ g.identify(!!0); };
    g.shutdown=function(){ g("rec",!1); };
    g.restart=function(){ g("rec",!0); };
    g.log = function(a,b){ g("log",[a,b]); };
    g.consent=function(a){ g("consent",!arguments.length||a); };
    g.identifyAccount=function(i,v){ o='account'; v=v||{}; v.acctId=i; g(o,v); };
    g.clearUserCookie=function(){};
    g.setVars=function(n, p){ g('setVars',[n,p]); };
    g._w={};
    y='XMLHttpRequest';
    g._w[y]=m[y];
    y='fetch';
    g._w[y]=m[y];
    if(m[y]) m[y]=function(){ return g._w[y].apply(this,arguments); };
    g._v="1.3.0";
  })(window,document,window['_fs_namespace'],'script','user');
</script>
```

Replace `YOUR_ORG_ID` with the organisation ID from your Subtext dashboard. If you used the setup wizard, this replacement was made automatically.

## How to detect if the snippet is already installed

Before adding the snippet, check whether your app already loads `fs.js`. Look for any of these signals:

* Search your codebase for `edge.fullstory.com` or `_fs_org` — if either string appears, the snippet (or Fullstory's own snippet) is already present
* Open your browser's Network tab, load a page, and filter by `fs.js` — a `200` response confirms capture is running
* Check your `<head>` template, root layout file, or tag manager configuration for a script referencing `fullstory.com`

If the snippet is already present from a prior Fullstory install, Subtext can connect to those existing sessions — you do not need a second copy of the script.

## Framework installation guides

Where exactly the snippet goes depends on your framework's rendering model. Click the guide for your stack:

| Framework                | Where the snippet lives                             | Guide                                       |
| ------------------------ | --------------------------------------------------- | ------------------------------------------- |
| **Next.js (App Router)** | `app/layout.tsx`, inside `<head>` via next/script   | [next-app-router](/docs/install/next-app-router) |
| **React (CRA / custom)** | `public/index.html` `<head>`, or a layout component | [react](/docs/install/react)                     |
| **Vite**                 | `index.html` `<head>` in the project root           | [vite](/docs/install/vite)                       |
| **Remix**                | `app/root.tsx`, inside the `<head>` export          | [remix](/docs/install/remix)                     |
| **Plain HTML / other**   | Directly in your `<head>` template                  | [manual](/docs/install/manual)                   |

<Tip>
  For Single Page Apps (React, Vite, Remix), the snippet only needs to appear once — in the root HTML shell. You do not need to add it to individual route components. Client-side navigations are tracked automatically as part of the session.
</Tip>

## Next steps

Once the snippet is in place and you have deployed your app, sessions begin recording immediately. Your next step is connecting your AI coding agent to those sessions via the MCP server. See the [installation overview](/docs/install/overview) for the full picture, or jump straight to the MCP setup guide for your agent.
