# Install og-fox

og-fox serves managed Open Graph (social preview) images for every page of a website through one URL pattern. Once the pattern is in place, images are assigned, designed, and refreshed from the og-fox dashboard — the site never needs to redeploy.

## The URL contract

```
https://og-fox.com/a/{OG_FOX_SITE_KEY}{pathname}
```

- `{OG_FOX_SITE_KEY}` — the site key (looks like `fx_a1b2c3d4e5`), from the og-fox dashboard.
- `{pathname}` — the page's own path, echoed verbatim (`/blog/launch-notes`, `/pricing`, `/` for the homepage).
- Always returns a 1200×630 PNG. It never 404s and never blocks a page render — worst case is a branded fallback image.
- Query strings are ignored by default, so plain `pathname` is always correct.

## Setup

1. Get the site key from the dashboard (Coverage page → your website → Integration card), or ask the user for it.
2. Store it as an environment variable. **The exact variable name depends on the framework**, because any snippet that runs in (or is bundled for) the browser can only read env vars with that framework's public prefix — a plain `OG_FOX_SITE_KEY` is invisible there and the URL renders as `.../a/undefined/...`. The site key is public (not a secret), so exposing it via a public-prefixed var is safe. Set the name your framework's snippet below reads:

   | Framework | Env var to set | Read in snippet as |
   | --- | --- | --- |
   | Next.js App Router (server metadata) | `OG_FOX_SITE_KEY` | `process.env.OG_FOX_SITE_KEY` |
   | Next.js Pages Router (client) | `NEXT_PUBLIC_OG_FOX_SITE_KEY` | `process.env.NEXT_PUBLIC_OG_FOX_SITE_KEY` |
   | Astro (server frontmatter) | `OG_FOX_SITE_KEY` | `import.meta.env.OG_FOX_SITE_KEY` |
   | Vite / Remix / React Router | `VITE_OG_FOX_SITE_KEY` | `import.meta.env.VITE_OG_FOX_SITE_KEY` |
   | SvelteKit | `PUBLIC_OG_FOX_SITE_KEY` | `$env/static/public` |
   | Nuxt | `NUXT_PUBLIC_OG_FOX_SITE_KEY` | `useRuntimeConfig().public.ogFoxSiteKey` |

   Put it in `.env` / `.env.local` and in the deployment platform's env settings. For purely static builds you may inline the key literally instead.
3. Apply the framework-specific edit below so every page's `og:image` (and `twitter:image`) points at the URL pattern with that page's pathname. Each snippet reads the env var named for its framework in the table above — keep the two in sync.

## Framework instructions

### Next.js (App Router)

Option A — with the SDK:

```bash
npm install og-fox
```

```ts
// app/blog/[slug]/page.tsx (repeat per route, passing the route's pathname)
import { ogMetadata } from 'og-fox/next'

export async function generateMetadata({ params }) {
  const { slug } = await params
  return {
    title: '…',
    ...ogMetadata(`/blog/${slug}`),
  }
}
```

Option B — zero dependencies:

```ts
export async function generateMetadata({ params }) {
  const { slug } = await params
  const pathname = `/blog/${slug}`
  return {
    openGraph: {
      images: [
        {
          url: `https://og-fox.com/a/${process.env.OG_FOX_SITE_KEY}${pathname}`,
          width: 1200,
          height: 630,
        },
      ],
    },
    twitter: { card: 'summary_large_image' },
  }
}
```

For static routes (e.g. `app/pricing/page.tsx`), export a static `metadata` object with the literal pathname instead.

### Next.js (Pages Router)

Add a shared SEO component that reads the real path from `useRouter().asPath` (`router.pathname` is the route *pattern* like `/blog/[slug]`, not the actual URL), then render it in every page:

```tsx
// components/SeoImage.tsx
import Head from 'next/head'
import { useRouter } from 'next/router'

export function SeoImage() {
  const router = useRouter()
  const pathname = router.asPath.split('?')[0] // strip query string
  const siteKey = process.env.NEXT_PUBLIC_OG_FOX_SITE_KEY
  return (
    <Head>
      <meta property="og:image" content={`https://og-fox.com/a/${siteKey}${pathname}`} />
      <meta name="twitter:card" content="summary_large_image" />
    </Head>
  )
}
```

Render `<SeoImage />` from each page (or from a shared layout / `pages/_app.tsx`).

### Astro

```astro
---
// src/layouts/Layout.astro
const ogImage = `https://og-fox.com/a/${import.meta.env.OG_FOX_SITE_KEY}${Astro.url.pathname}`
---
<meta property="og:image" content={ogImage} />
<meta name="twitter:card" content="summary_large_image" />
```

### SvelteKit

```svelte
<!-- src/routes/+layout.svelte -->
<script>
  import { page } from '$app/state'
  import { PUBLIC_OG_FOX_SITE_KEY } from '$env/static/public'
  const ogImage = `https://og-fox.com/a/${PUBLIC_OG_FOX_SITE_KEY}${page.url.pathname}`
</script>

<svelte:head>
  <meta property="og:image" content={ogImage} />
  <meta name="twitter:card" content="summary_large_image" />
</svelte:head>
```

### Nuxt

First declare the key in `nuxt.config.ts` so `useRuntimeConfig().public.ogFoxSiteKey` exists (it is auto-populated at runtime from the `NUXT_PUBLIC_OG_FOX_SITE_KEY` env var):

```ts
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      ogFoxSiteKey: '', // ← filled from NUXT_PUBLIC_OG_FOX_SITE_KEY
    },
  },
})
```

Then, in `app.vue` or a layout:

```ts
// pathname from useRoute()
const route = useRoute()
const config = useRuntimeConfig()
useSeoMeta({
  ogImage: `https://og-fox.com/a/${config.public.ogFoxSiteKey}${route.path}`,
  twitterCard: 'summary_large_image',
})
```

### Remix / React Router

```ts
// In each route's meta export
export const meta = ({ location }) => [
  {
    property: 'og:image',
    content: `https://og-fox.com/a/${import.meta.env.VITE_OG_FOX_SITE_KEY}${location.pathname}`,
  },
  { name: 'twitter:card', content: 'summary_large_image' },
]
```

### Plain HTML

Add to each page's `<head>`, substituting the page's own path:

```html
<meta property="og:image" content="https://og-fox.com/a/fx_YOUR_KEY/about" />
<meta name="twitter:card" content="summary_large_image" />
```

The homepage uses `https://og-fox.com/a/fx_YOUR_KEY/`.

## Verify the integration

```bash
curl -sI "https://og-fox.com/a/$OG_FOX_SITE_KEY/"
```

Expect: `HTTP 200`, `Content-Type: image/png`, and an `X-Cache` header of `MISS` (first hit), `HIT`, or `STALE`. Then confirm a rendered page's `<meta property="og:image">` contains `/a/` followed by the site key.

**Important — the endpoint always returns `HTTP 200` `image/png`, even when misconfigured.** A wrong or typo'd site key, or an unregistered domain, returns a *branded fallback* image with an `X-Cache` value of `NOT_FOUND`, `DISABLED`, `UNPAID`, or `ERROR`. If you see any of those (i.e. anything other than `MISS` / `HIT` / `STALE`), the integration is **not** working — do not report success. Re-check the site key and confirm the domain is registered in the dashboard.

## When done

Report back: the list of pages/routes now covered, one example image URL, and any routes intentionally skipped.
