> ## 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.

# Install the Subtext Snippet in Your Remix Application

> Add the Subtext capture snippet to your Remix app by inserting an inline script into your root route using dangerouslySetInnerHTML.

In Remix, `app/root.tsx` is the single file that renders the full HTML document for every route in your application. It exposes a `<Scripts />` component that Remix uses to inject your app's JavaScript — and it is the right place to add the Subtext capture snippet so it loads on every page. You will add an inline `<script>` tag just before `<Scripts />` inside the `<body>`.

<Note>
  You can also run `npx @subtextdev/subtext-wizard` in your project root to have Subtext detect your framework and insert the snippet automatically. Follow this guide if you prefer to wire things up by hand.
</Note>

<Steps>
  <Step title="Find your Org ID">
    Log in to Subtext and open **Settings**. Your Org ID is listed in the **Capture** section. Copy it — you will paste it in place of `YOUR_ORG_ID` in the snippet below.
  </Step>

  <Step title="Open app/root.tsx">
    Open `app/root.tsx` (or `app/root.jsx`) in your editor. Look for the `App` component (or whichever component exports `<html>…</html>`) — this is where you will add the snippet.
  </Step>

  <Step title="Add the capture snippet before Scripts">
    Insert a `<script>` tag containing the capture snippet just before the `<Scripts />` component inside the `<body>`. Replace `YOUR_ORG_ID` with the Org ID you copied from Subtext settings.

    ```tsx app/root.tsx theme={null}
    import {
      Links,
      Meta,
      Outlet,
      Scripts,
      ScrollRestoration,
    } from '@remix-run/react';

    export default function App() {
      return (
        <html lang="en">
          <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <Meta />
            <Links />
          </head>
          <body>
            <Outlet />
            <ScrollRestoration />

            {/* Subtext capture snippet */}
            <script
              dangerouslySetInnerHTML={{
                __html: `
                  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){var s,f,a=function(h){return!(h in m)||(m.console&&m.console.log&&m.console.log('FullStory namespace conflict. More info: https://www.fullstory.com/developer/ns.html'),!1)};if(a(l)&&a(n)){m[l]={q:[]};var d=function(){return Array.prototype.slice.call(arguments,0)};m[l].identify=function(){m[l].q.push(["identify"].concat(d.apply(null,arguments)))};m[l].setUserVars=function(){m[l].q.push(["setUserVars"].concat(d.apply(null,arguments)))};m[l].event=function(){m[l].q.push(["event"].concat(d.apply(null,arguments)))};m[l].anonymize=function(){m[l].q.push(["anonymize"].concat(d.apply(null,arguments)))};m[l].shutdown=function(){m[l].q.push(["shutdown"].concat(d.apply(null,arguments)))};m[l].restart=function(){m[l].q.push(["restart"].concat(d.apply(null,arguments)))};m[l].log=function(a,b){m[l].q.push(["log",a,b])};m[l].consent=function(){m[l].q.push(["consent"].concat(d.apply(null,arguments)))};m[l].identifyAccount=function(){m[l].q.push(["identifyAccount"].concat(d.apply(null,arguments)))};m[l].clearUserCookie=function(){m[l].q.push(["clearUserCookie"].concat(d.apply(null,arguments)))};m[l].setVars=function(){m[l].q.push(["setVars"].concat(d.apply(null,arguments)))};m[l]._w={};y="XMLHttpRequest";m[l]._w[y]=m[y];y="fetch";m[l]._w[y]=m[y];if(m[y])m[l]._w[y]=m[y];m[l]._v="1.3.0";g=n.createElement(e);g.async=1;g.crossOrigin="anonymous";g.src='https://'+m[_fs_script];y=n.getElementsByTagName(e)[0];y.parentNode.insertBefore(g,y)}}(window,document,"script","_fs_config",n,o,g,y);
                `,
              }}
            />

            <Scripts />
          </body>
        </html>
      );
    }
    ```

    <Note>
      Remix performs server-side rendering, so you must use `dangerouslySetInnerHTML` to embed an inline script. This is safe here because the snippet content is static and does not include any user-generated input.
    </Note>
  </Step>

  <Step title="Start your dev server">
    Run `npm run dev` and open your application in a browser. The snippet is injected server-side into every response, so it will fire on initial page load as well as on client-side navigations.
  </Step>

  <Step title="Verify capture is working">
    Navigate to a few routes, then open **Subtext → Sessions**. A new session should appear within a minute or two.

    <Tip>
      Sessions typically appear within 30–60 seconds of page load. If nothing shows up, confirm that `YOUR_ORG_ID` was replaced with your actual Org ID and that the snippet is in the component that renders the full `<html>` document (usually the default export of `app/root.tsx`).
    </Tip>
  </Step>
</Steps>

<Note>
  The snippet records full production sessions and masks all form inputs by default. Your agent can flag additional PII and propose masking rules for your review from within Subtext.
</Note>
