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

# Rendering SEO data

> Render meta tags, Open Graph, & Twitter Cards from ButterCMS in React/Next.js, Angular, Jekyll, or Vue/Nuxt.js.

# Rendering SEO data

This guide covers how to render SEO data from the ButterCMS API in your frontend framework, including meta tags, Open Graph tags, and Twitter Cards.

## Framework implementations

### React / Next.js

#### Reusable SEO Component

You can use the `next/head` submodule to encapsulate modifications to the head element. Normally, you would have an SEO React component that would do this for you:

```javascript theme={null}
// components/seo.js
import React from 'react'
import Head from 'next/head'

function SEO ({ description, title }) {
  return (
    <Head>
      <title>{title}</title>
      <meta name='description' content={description} />
      <meta property='og:type' content='website' />
      <meta property='og:title' content={title} />
      <meta property='og:description' content={description} />
      <meta name='twitter:card' content='summary' />
      <meta name='twitter:title' content={title} />
      <meta name='twitter:description' content={description} />
    </Head>
  )
}

export default SEO
```

Then use this component in each of your pages:

```javascript theme={null}
// pages/index.js
import SEO from '../components/seo'

const Index = () => (
  <div>
    <SEO title='sample title' description='sample description' />
    <p>Hello Next.js</p>
  </div>
)

export default Index
```

#### Per-Page implementation with Open Graph

```jsx theme={null}
import Head from 'next/head';

function BlogPost({ post }) {
  const fullUrl = `https://yoursite.com/blog/${post.slug}`;

  return (
    <>
      <Head>
        {/* Open Graph */}
        <meta property="og:type" content="article" />
        <meta property="og:url" content={fullUrl} />
        <meta property="og:title" content={post.seo_title || post.title} />
        <meta property="og:description" content={post.meta_description} />
        <meta property="og:image" content={post.featured_image} />
        <meta property="og:site_name" content="Your Site Name" />

        {/* Twitter Card */}
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:site" content="@YourHandle" />
        <meta name="twitter:title" content={post.seo_title || post.title} />
        <meta name="twitter:description" content={post.summary} />
        <meta name="twitter:image" content={post.featured_image} />
        <meta name="twitter:image:alt" content={post.featured_image_alt} />
      </Head>
      {/* Page content */}
    </>
  );
}
```

### Angular

Meta tags like the page title and Open Graph tags play a big role in SEO. In Angular SPAs, you must update these tags dynamically as users navigate across different routes and make sure they're ready when crawlers arrive.

Angular provides the **Title** and **Meta** services to handle this:

```typescript theme={null}
import { Component, OnInit } from '@angular/core';
import { Title, Meta } from '@angular/platform-browser';

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
})
export class AboutComponent implements OnInit {
  constructor(private title: Title, private meta: Meta) {}

  ngOnInit(): void {
    this.title.setTitle('About Us - My Angular App');
    this.meta.updateTag({
      name: 'description',
      content: 'Learn more about our company, team, and mission.'
    });
    this.meta.updateTag({
      property: 'og:title',
      content: 'About Us - My Angular App'
    });
    this.meta.updateTag({
      property: 'og:description',
      content: 'Learn more about our company, team, and mission.'
    });
  }
}
```

### Jekyll

In Jekyll, each page is compiled from a markdown file. The header of the markdown file will contain the title and description variables that will be used in the template:

```markdown theme={null}
---
layout: post
title:  "Welcome to Jekyll!"
description: "Sample description"
date:   2019-08-28 00:22:09 +0530
categories: jekyll update
---

Some page content...
```

The default layout template uses these variables:

```html theme={null}
<head>
  <title>{{ page.title }}</title>
  <meta name="description" content="{{ page.description }}" />
  <meta name="og:title" content="{{ page.title }}" />
  <meta name="og:description" content="{{ page.description }}" />
</head>
```

### Vue / Nuxt.js

```vue theme={null}
<script setup>
const post = await useButter().post.retrieve('my-post-slug');

useHead({
  meta: [
    // Open Graph
    { property: 'og:type', content: 'article' },
    { property: 'og:url', content: `https://yoursite.com/blog/${post.slug}` },
    { property: 'og:title', content: post.seo_title || post.title },
    { property: 'og:description', content: post.meta_description },
    { property: 'og:image', content: post.featured_image },

    // Twitter Card
    { name: 'twitter:card', content: 'summary_large_image' },
    { name: 'twitter:site', content: '@YourHandle' },
    { name: 'twitter:title', content: post.seo_title || post.title },
    { name: 'twitter:description', content: post.summary },
    { name: 'twitter:image', content: post.featured_image },
  ]
});
</script>
```

## Using ButterCMS API data for meta tags

When fetching content from ButterCMS, you'll have access to SEO fields that you can use to populate your meta tags:

```javascript theme={null}
// Fetching a blog post
const response = await butter.post.retrieve('my-post-slug');
const post = response.data.data;

// Available SEO fields
const seoData = {
  title: post.seo_title,           // SEO-optimized title
  description: post.meta_description, // Meta description
  slug: post.slug,                 // URL slug
  featuredImage: post.featured_image, // For og:image
  featuredImageAlt: post.featured_image_alt
};
```

For Pages with an SEO component:

```javascript theme={null}
// Fetching a page with SEO component
const response = await butter.page.retrieve('*', 'my-page-slug');
const page = response.data.data;

// Access SEO component fields
const seoData = {
  title: page.fields.seo.title,
  description: page.fields.seo.description,
  ogImage: page.fields.seo.og_image,
  canonical: page.fields.seo.canonical_url
};
```

## Creating an SEO Component in ButterCMS

Create a reusable SEO & Social component to give editors control over social sharing:

```
SEO & Social Component:
├── meta_title (Short Text) - max 60 chars
├── meta_description (Long Text) - max 160 chars
├── og_title (Short Text) - Falls back to meta_title
├── og_description (Long Text) - Falls back to meta_description
├── og_image (Media) - Recommended: 1200x630px
├── og_image_alt (Short Text)
├── twitter_title (Short Text) - Falls back to og_title
├── twitter_description (Long Text) - Falls back to og_description
├── twitter_image (Media) - Can differ from og_image
├── twitter_handle (Short Text) - Author's Twitter handle
└── canonical_url (Short Text) - Optional override
```

Then in your template:

```javascript theme={null}
function getSocialMeta(seoComponent, defaults) {
  return {
    ogTitle: seoComponent?.og_title || seoComponent?.meta_title || defaults.title,
    ogDescription: seoComponent?.og_description || seoComponent?.meta_description || defaults.description,
    ogImage: seoComponent?.og_image || defaults.image,
    twitterTitle: seoComponent?.twitter_title || seoComponent?.og_title || seoComponent?.meta_title || defaults.title,
    twitterDescription: seoComponent?.twitter_description || seoComponent?.og_description || defaults.description,
    twitterImage: seoComponent?.twitter_image || seoComponent?.og_image || defaults.image,
  };
}
```

<Tip>Ready to add social sharing buttons to your site? See [Add Social Sharing Buttons](../how-to-recipes/add-a-feature/social-sharing-buttons) for a step-by-step guide.</Tip>
