# Astro

## Getting Started

## Create a connection

1. Sign in to [OpenGraph+](/)
2. Go to your website's **Meta Tags** page
3. Create a new connection and copy your connection URL

Your connection URL looks like `https://$OGPLUS_KEY.ogplus.net`.

## Find your layout

Open `src/layouts/Layout.astro`. Astro pages use layouts via the `layout` frontmatter property or by wrapping content in a layout component. This is where you'll add Open Graph meta tags so every page inherits them.

## Add Open Graph meta tags

Add the full set of Open Graph tags to your layout's `<head>`. Use props to pull in dynamic values from each page:

```html
---
// src/layouts/Layout.astro
const { title, description } = Astro.props
const siteUrl = 'https://mysite.com'
---
<html>
  <head>
    <meta property="og:title" content={title || 'My Site'} />
    <meta property="og:description" content={description || 'Welcome to my site'} />
    <meta property="og:url" content={`${siteUrl}${Astro.url.pathname}`} />
    <meta property="og:site_name" content="My Site" />
    <meta property="og:type" content="website" />
    <meta property="og:image" content={`https://$OGPLUS_KEY.ogplus.net${Astro.url.pathname}`} />
    <meta name="twitter:card" content="summary_large_image" />
  </head>
  <body>
    <slot />
  </body>
</html>
```

Replace `https://$OGPLUS_KEY.ogplus.net` with the connection URL you copied.

### Tag reference

| Tag | Value | Source |
|-----|-------|--------|
| `og:title` | Page title | `title` prop from each page |
| `og:description` | Page description | `description` prop from each page |
| `og:url` | Canonical URL | `siteUrl` + `Astro.url.pathname` |
| `og:site_name` | Site name | Hardcoded in layout |
| `og:type` | Content type | `website` (override per page if needed) |
| `og:image` | Social card image | OpenGraph+ connection URL + page path |
| `twitter:card` | Card format | `summary_large_image` for large previews |

## Dynamic tags from pages

Astro pages pass props to layouts. Here's a dynamic blog post page:

```html
---
// src/pages/posts/[slug].astro
import Layout from '../../layouts/Layout.astro'
import { getPost } from '../../lib/posts'

const { slug } = Astro.params
const post = await getPost(slug)
---
<Layout title={post.title} description={post.excerpt}>
  <h1>{post.title}</h1>
  <p>{post.body}</p>
</Layout>
```

For content collections:

```html
---
// src/pages/blog/[...slug].astro
import Layout from '../../layouts/Layout.astro'
import { getCollection } from 'astro:content'

const { slug } = Astro.params
const post = (await getCollection('blog')).find(p => p.slug === slug)
---
<Layout title={post.data.title} description={post.data.description}>
  <!-- content -->
</Layout>
```

## Static and SSR

This works with both Astro's default static output and SSR mode. In static mode, the meta tags are baked into every page at build time. In SSR mode, they're generated per-request.

## Verify

Deploy your site and open the preview tool in your OpenGraph+ dashboard. Paste a URL from your site to confirm the meta tags and social card image render correctly.


## Customize

OpenGraph+ captures your page in a headless browser and renders it as an image. You control what gets captured using meta tags in your layout and CSS in your stylesheets.

All of the rendering options below are standard HTML meta tags and CSS, so they work the same regardless of framework. The [HTML, CSS, & HTTP guide](/docs/html-css) covers each one in detail. This page shows how to wire them into an Astro site.

## Meta tags

Add these to your layout's `<head>` alongside your `og:image` tag. They're all optional.

```html
---
// src/layouts/Layout.astro
---
<head>
  <meta property="og:image" content={`https://$OGPLUS_KEY.ogplus.net${Astro.url.pathname}`} />
  <meta name="twitter:card" content="summary_large_image" />

  <!-- Render at 800px wide instead of the default -->
  <meta property="og:plus:viewport:width" content="800" />

  <!-- Only capture this element instead of the full page -->
  <meta property="og:plus:selector" content=".post-header" />

  <!-- Inject inline styles on the captured element -->
  <meta property="og:plus:style" content="padding: 60px; background: #0f172a; color: white;" />
</head>
```

See the [Rendering](/docs/html-css/rendering) guide for what each meta tag does and how they interact.

## CSS styling

OpenGraph+ adds a `data-ogplus` attribute to your `<html>` element during capture. Use it in your stylesheets to hide navigation, adjust spacing, or restyle anything for the social card without affecting your actual site.

```css
/* src/styles/global.css */
html[data-ogplus] {
  nav { display: none; }
  footer { display: none; }
  .hero { padding: 60px; }
}
```

If you're using Tailwind, there's a plugin that gives you `ogplus:` variants like `ogplus:hidden` and `ogplus-twitter:bg-sky-500`. See [CSS Styling](/docs/html-css/data-attributes) for plain CSS examples and [Tailwind setup](/docs/html-css/data-attributes#tailwind-css).

## Templates

For fully custom social card layouts that pull content from your page, use `<template>` elements. These let you build a completely different layout for screenshots without touching your visible page.

```html
<!-- src/layouts/Layout.astro -->
<template id="ogplus">
  <div style="padding: 48px; background: #0f172a; color: white; height: 100%;">
    <h1 style="font-size: 48px;">
      ${document.querySelector('h1')?.textContent}
    </h1>
  </div>
</template>
```

See the [Templates](/docs/html-css/templates) guide for expression syntax, platform-specific templates, and full examples.

## Testing locally

The [Preview Bookmarklet](/docs/html-css/bookmarklet) sets the `data-ogplus` attribute in your browser so you can see how your CSS and Tailwind variants look without deploying or waiting for a real crawler to hit your page.

## Full example

A layout with all the pieces together:

```html
<head>
  <meta property="og:title" content={title || 'My Site'} />
  <meta property="og:description" content={description || 'Welcome'} />
  <meta property="og:image" content={`https://$OGPLUS_KEY.ogplus.net${Astro.url.pathname}`} />
  <meta name="twitter:card" content="summary_large_image" />
  <meta property="og:plus:selector" content=".post-header" />
  <meta property="og:plus:style" content="padding: 60px; background-color: #0f172a; color: white;" />
  <meta property="og:plus:viewport:width" content="800" />
</head>
```


## Caching

OpenGraph+ reads HTTP cache headers from your pages to decide when to re-render social card images. Since Astro can run in both static and SSR modes, how you configure caching depends on your setup.

This page covers the Astro side. For how OpenGraph+ handles caching at the HTTP level, see the [HTTP Caching](/docs/html-css/caching) guide.

## Static mode (default)

Astro generates static HTML by default. Cache headers come from your hosting provider, not Astro itself.

### Netlify / Cloudflare Pages

Create a `_headers` file in your `public/` directory:

```
# public/_headers
/*
  Cache-Control: public, max-age=604800
```

### Vercel

Add headers to your `vercel.json`:

```json
{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "Cache-Control", "value": "public, max-age=604800" }
      ]
    }
  ]
}
```

## SSR mode

With `output: 'server'` or `output: 'hybrid'`, you can set cache headers directly in your pages:

```html
---
Astro.response.headers.set('Cache-Control', 'public, max-age=86400')
---
<html>
  <head>...</head>
  <body>...</body>
</html>
```

This gives you per-page control over caching.

## Meta tag overrides

If you can't control HTTP headers (or want per-page control in static mode), use meta tags directly in your HTML:

```html
<meta property="og:plus:cache:max_age" content="86400">
<meta property="og:plus:cache:etag" content="v1.2.3">
```

When the etag changes, OpenGraph+ re-renders even if the cache hasn't expired. These meta tags take priority over HTTP headers.

See the [HTTP Caching guide](/docs/html-css/caching) for the full reference on cache headers and meta tag overrides.

## Purging

Force an immediate re-render from the OpenGraph+ dashboard by purging the cached image for any URL.

## Recommendations

| Page type | Strategy |
|-----------|----------|
| Static pages | Long TTL via hosting headers (7 days) |
| Blog posts | Medium TTL (1 day) + etag meta tag |
| Dynamic SSR pages | `Cache-Control` header per-page (1-24 hours) |
| Frequently updated | Short TTL + etag for instant invalidation |


## Troubleshooting

## Meta tags not appearing

Check that the meta tags are in the `<head>` of your layout file. View the page source (not browser DevTools) to confirm the tags are present in the raw HTML.

If you're using multiple layouts, make sure the `og:image` and `twitter:card` meta tags are included in each one.

## Wrong image showing

Social platforms and OpenGraph+ cache images. If you've updated your page but see the old image:

1. Purge the cached image from the OpenGraph+ dashboard
2. Wait a few minutes for the new image to render
3. Clear the platform's cache using their debugger tools (see below)

## Client-side islands

Astro components render as static HTML by default, which is ideal for social cards. Interactive islands (`client:load`, `client:visible`, etc.) execute JavaScript after the initial HTML loads.

OpenGraph+ captures the initial HTML, so content inside interactive islands may not appear in your social card. Keep important social card content outside of client-side islands, or ensure it's included in the server-rendered HTML.

## Social platforms not updating

Social networks cache images aggressively. After purging from OpenGraph+, use each platform's debugger to force a refresh:

- **Facebook**: [Sharing Debugger](https://developers.facebook.com/tools/debug/)
- **Twitter/X**: [Card Validator](https://cards-dev.twitter.com/validator)
- **LinkedIn**: [Post Inspector](https://www.linkedin.com/post-inspector/)

## Purging

To force OpenGraph+ to re-render an image:

1. Go to your OpenGraph+ dashboard
2. Navigate to the website's cache page
3. Enter the URL and purge

## Testing

Use the preview tool in your OpenGraph+ dashboard to test how any page on your site renders as a social card. Paste a URL and see the result immediately.

