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

> ## Agent Instructions
> Outlit is the product that monitors customers and completes approved customer work. The customer context interfaces are the API, CLI, MCP server, and skills. Canonical references on this site: /api-reference/introduction for API concepts and authentication, /openapi.json for the public API contract, /cli/overview for the CLI, /ai-integrations/mcp for MCP. Prefer the `outlit` CLI or MCP tools for agent tasks; cite page sources when answering.

# Astro

> Integrate Outlit tracking with Astro applications.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install --save @outlit/browser
  ```

  ```bash pnpm theme={null}
  pnpm add @outlit/browser
  ```

  ```bash yarn theme={null}
  yarn add @outlit/browser
  ```

  ```bash bun theme={null}
  bun add @outlit/browser
  ```
</CodeGroup>

## Quick start

Add Outlit to your base layout:

```astro theme={null}
---
// src/layouts/BaseLayout.astro
const PUBLIC_OUTLIT_KEY = import.meta.env.PUBLIC_OUTLIT_KEY
---

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>My Site</title>
  </head>
  <body>
    <slot />

    <script>
      import outlit from '@outlit/browser'

      outlit.init({
        publicKey: import.meta.env.PUBLIC_OUTLIT_KEY,
        trackPageviews: true,
      })
    </script>
  </body>
</html>
```

Add your public key to `.env`:

```
PUBLIC_OUTLIT_KEY=pk_your_public_key_here
```

## Alternative: inline script

Use an inline script if you prefer an inline setup:

```astro theme={null}
// src/layouts/BaseLayout.astro

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>My Site</title>
  </head>
  <body>
    <slot />

    <script>
      import outlit from '@outlit/browser'

      outlit.init({
        publicKey: import.meta.env.PUBLIC_OUTLIT_KEY,
        trackPageviews: true,
      })
    </script>
  </body>
</html>
```

## Tracking events

Track events in Astro components:

```astro theme={null}
// src/components/PricingButton.astro

<button id="pricing-btn" data-plan="pro">
  View Pricing
</button>

<script>
  import outlit from '@outlit/browser'

  const button = document.getElementById('pricing-btn')

  button?.addEventListener('click', () => {
    const plan = button.getAttribute('data-plan')
    outlit.track('pricing_clicked', { plan })
  })
</script>
```

### With client directives

Use client directives for interactive components:

```astro theme={null}
// src/pages/index.astro
import PricingButton from '../components/PricingButton.jsx'

<html>
  <body>
    <PricingButton client:load />
  </body>
</html>
```

## Framework islands

### React island

```jsx theme={null}
// src/components/PricingButton.jsx
import { useEffect } from 'react'
import outlit from '@outlit/browser'

export default function PricingButton() {
  function handleClick() {
    outlit.track('pricing_clicked', { plan: 'pro' })
  }

  return (
    <button onClick={handleClick}>
      View Pricing
    </button>
  )
}
```

### Vue island

```vue theme={null}
<!-- src/components/PricingButton.vue -->
<script setup>
import outlit from '@outlit/browser'

function handleClick() {
  outlit.track('pricing_clicked', { plan: 'pro' })
}
</script>

<template>
  <button @click="handleClick">
    View Pricing
  </button>
</template>
```

### Svelte island

```svelte theme={null}
<!-- src/components/PricingButton.svelte -->
<script>
  import outlit from '@outlit/browser'

  function handleClick() {
    outlit.track('pricing_clicked', { plan: 'pro' })
  }
</script>

<button on:click={handleClick}>
  View Pricing
</button>
```

## Identifying users

Identify users after authentication:

```astro theme={null}
// src/components/LoginForm.astro

<form id="login-form">
  <input type="email" name="email" placeholder="Email" />
  <input type="password" name="password" placeholder="Password" />
  <button type="submit">Login</button>
</form>

<script>
  import outlit from '@outlit/browser'

  const form = document.getElementById('login-form')

  form?.addEventListener('submit', async (e) => {
    e.preventDefault()

    const formData = new FormData(e.target)
    const email = formData.get('email')
    const password = formData.get('password')

    const response = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password })
    })

    const user = await response.json()

    outlit.identify({
      email: user.email,
      userId: user.id,
      traits: {
        name: user.name,
        plan: user.plan
      }
    })

    window.location.href = '/dashboard'
  })
</script>
```

## Auth state management

For apps with authentication, use `setUser()` and `clearUser()` to sync user identity:

```astro theme={null}
// src/components/AuthObserver.astro

<script>
  import outlit from '@outlit/browser'

  // Check for existing auth session on page load
  async function checkAuth() {
    const response = await fetch('/api/auth/session')
    const session = await response.json()

    if (session?.user) {
      outlit.setUser({
        email: session.user.email,
        userId: session.user.id,
        traits: {
          name: session.user.name,
          plan: session.user.plan
        }
      })
    }
  }

  checkAuth()

  // Listen for auth state changes (e.g., from Supabase, Firebase)
  window.addEventListener('auth-state-changed', (event) => {
    const user = event.detail?.user
    if (user) {
      outlit.setUser({
        email: user.email,
        userId: user.id
      })
    } else {
      outlit.clearUser()
    }
  })
</script>
```

Using `setUser()` ensures the user identity persists across page navigations and is properly set when users return to your site already logged in.

## View transitions

Track navigation with Astro's View Transitions:

```astro theme={null}
// src/layouts/BaseLayout.astro
import { ViewTransitions } from 'astro:transitions'

<!DOCTYPE html>
<html lang="en">
  <head>
    <ViewTransitions />
  </head>
  <body>
    <slot />

    <script>
      import outlit from '@outlit/browser'

      outlit.init({
        publicKey: import.meta.env.PUBLIC_OUTLIT_KEY,
        trackPageviews: false, // Manual tracking
      })

      // Track pageviews (fires on initial load and navigation with View Transitions)
      document.addEventListener('astro:page-load', () => {
        outlit.track('pageview', {
          path: window.location.pathname
        })
      })
    </script>
  </body>
</html>
```

## API routes

Track events from API endpoints:

```typescript theme={null}
// src/pages/api/checkout.ts
import type { APIRoute } from 'astro'
import { Outlit } from '@outlit/node'

const outlit = new Outlit({
  publicKey: import.meta.env.OUTLIT_KEY
})

export const POST: APIRoute = async ({ request }) => {
  const body = await request.json()

  outlit.track({
    userId: body.userId,
    eventName: 'checkout_completed',
    properties: {
      amount: body.amount,
      plan: body.plan
    }
  })

  await outlit.flush()

  return new Response(JSON.stringify({ success: true }), {
    status: 200,
    headers: { 'Content-Type': 'application/json' }
  })
}
```

For server-side tracking, see the [Node.js integration guide](/tracking/server/nodejs).

## SSR pages

For SSR pages, ensure tracking runs client-side:

```astro theme={null}
// src/pages/products/[id].astro
export const prerender = false // SSR mode

const { id } = Astro.params
const product = await fetchProduct(id)

<html>
  <body>
    <h1>{product.name}</h1>
    <div id="product-data" data-product-id={id} style="display: none;"></div>

    <script>
      import outlit from '@outlit/browser'

      // Track product view
      const productId = document.getElementById('product-data')?.dataset.productId
      outlit.track('product_viewed', {
        productId
      })
    </script>
  </body>
</html>
```

## Content collections

Track interactions with content:

```astro theme={null}
// src/pages/blog/[...slug].astro
import { getEntry } from 'astro:content'

const entry = await getEntry('blog', Astro.params.slug)

<article>
  <h1>{entry.data.title}</h1>
  <div set:html={entry.body} />
  <div id="blog-data" data-slug={Astro.params.slug} data-title={entry.data.title} style="display: none;"></div>
</article>

<script>
  import outlit from '@outlit/browser'

  const blogData = document.getElementById('blog-data')
  outlit.track('blog_post_viewed', {
    slug: blogData?.dataset.slug,
    title: blogData?.dataset.title
  })
</script>
```

## Activation event

Track your value milestone as an ordinary event once it succeeds:

```astro theme={null}
// src/components/OnboardingComplete.astro
<script>
  import outlit from '@outlit/browser'

  // Call this from your app's confirmed onboarding success callback.
  function onOnboardingCompleted() {
    outlit.track('onboarding_completed', { flow: 'onboarding', step: 'completed' })
  }
</script>
```

Choose this event as your activation event in Outlit. The first matching event records activation once for each eligible resolved person and company, independently. Billing status comes from verified integrations such as Stripe. See [Customer journey](/concepts/customer-journey).

## Consent management

The default `autoTrack: "auto"` mode already keeps tracking off for visitors in regions that require opt-in consent (EEA, UK, Switzerland, or unknown); the example below uses `autoTrack: false` to wait for an explicit consent signal unless a previous opt-in is stored.

Handle user consent:

```astro theme={null}
// src/components/CookieBanner.astro

<div id="cookie-banner" style="display: none;">
  <p>We use cookies to improve your experience.</p>
  <button id="accept-btn">Accept</button>
</div>

<script>
  import outlit from '@outlit/browser'

  // Initialize without auto-tracking
  outlit.init({
    publicKey: import.meta.env.PUBLIC_OUTLIT_KEY,
    autoTrack: false
  })

  // Check consent
  const hasConsent = localStorage.getItem('tracking-consent')
  if (hasConsent === 'true') {
    outlit.enableTracking()
  } else {
    document.getElementById('cookie-banner').style.display = 'block'
  }

  // Handle accept
  document.getElementById('accept-btn')?.addEventListener('click', () => {
    localStorage.setItem('tracking-consent', 'true')
    outlit.enableTracking()
    document.getElementById('cookie-banner').style.display = 'none'
  })
</script>

<style>
  #cookie-banner {
    position: fixed;
    bottom: 1rem;
    left: 1rem;
    right: 1rem;
    padding: 1rem;
    background: white;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    border-radius: 0.5rem;
  }
</style>
```

## Environment variables

Configure environment variables:

```bash theme={null}
# .env

# Public key (used in browser)
PUBLIC_OUTLIT_KEY=pk_your_public_key_here

# Server key (used in API routes)
OUTLIT_KEY=pk_your_public_key_here
```

<Warning>
  Keep server environment variables out of client-side code. Only use `OUTLIT_KEY` in API routes.
</Warning>

## TypeScript support

Full TypeScript support is included:

```typescript theme={null}
import outlit, { type OutlitOptions } from '@outlit/browser'

const options: OutlitOptions = {
  publicKey: import.meta.env.PUBLIC_OUTLIT_KEY,
  trackPageviews: true,
  flushInterval: 5000
}

outlit.init(options)
```

## Next steps

<CardGroup cols={2}>
  <Card title="Server-side tracking" icon="server" href="/tracking/server/nodejs">
    Track events from your backend
  </Card>

  <Card title="Identity resolution" icon="fingerprint" href="/concepts/identity-resolution">
    Learn how visits and events link to people
  </Card>

  <Card title="npm package" icon="npm" href="/tracking/browser/npm">
    Full API reference
  </Card>

  <Card title="React integration" icon="react" href="/tracking/browser/react">
    Use Outlit with React
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.