Automatic Semantic HTML Generation

Learn about the automatic semantic HTML generation feature of the Nuxt AEO module

The Nuxt AEO module automatically generates semantic HTML along with Schema.org JSON-LD to optimize LLM crawling.

Overview

The automatic semantic HTML generation feature:

  • Automatically generates semantic HTML based on JSON-LD schema data
  • Generated HTML is hidden with display: none, but is included in the HTML source so LLMs and crawlers can read it
  • Using JSON-LD and semantic HTML together improves AI model content understanding

How It Works

1. JSON-LD Generation

First, Schema.org JSON-LD is injected into the <head> tag:

example.html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [...]
}
</script>

2. Semantic HTML Generation

When using the renderHtml: true option, semantic HTML is automatically generated based on schema data and injected into the <body> tag:

example.html
<div class="nuxt-aeo-semantic-faqpage nuxt-aeo-visually-hidden" aria-hidden="true">
  <div itemscope itemtype="https://schema.org/FAQPage">
    <div itemscope itemprop="mainEntity" itemtype="https://schema.org/Question">
      <h2 itemprop="name">Question</h2>
      <div itemprop="acceptedAnswer" itemscope itemtype="https://schema.org/Answer">
        <p itemprop="text">Answer</p>
      </div>
    </div>
  </div>
</div>

3. Visual Hiding

When visuallyHidden: true (default), the generated semantic HTML is hidden with the visually-hidden class:

example.css
.nuxt-aeo-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

Usage

Using with useSchemaFaq

useSchemaFaq is set to renderHtml: true by default:

pages/example.vue
<script setup lang="ts">
useSchemaFaq({
  mainEntity: [
    {
      name: 'Question 1',
      acceptedAnswer: {
        text: 'Answer 1',
      },
    },
  ],
  renderHtml: true, // Default: true
  visuallyHidden: true, // Default: true
})
</script>

Using with useSchema

useSchema is set to renderHtml: false by default. To generate semantic HTML, you must explicitly set renderHtml: true:

pages/example.vue
<script setup lang="ts">
useSchema({
  context: 'https://schema.org',
  type: 'Organization',
  name: 'My Company',
  url: 'https://example.com',
  renderHtml: true, // Generate semantic HTML
  visuallyHidden: true, // Visually hide
})
</script>

Global Configuration

You can configure globally in nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-aeo'],
  aeo: {
    schemas: [
      {
        type: 'Organization',
        name: 'My Company',
        url: '/', // Relative URL - will be normalized to absolute
        logo: '/images/logo.png', // Relative URL - will be normalized to absolute
        renderHtml: true, // Set on individual schema
        visuallyHidden: true,
      },
    ],
    renderHtml: true, // Global default value
    visuallyHidden: true, // Global default value
  },
})

Supported Schema Types

Semantic HTML is automatically generated for the following Schema types:

Person

pages/example.vue
<script setup lang="ts">
useSchema({
  context: 'https://schema.org',
  type: 'Person',
  name: 'John Doe',
  alternateName: 'JD',
  jobTitle: 'Software Engineer',
  url: '/profile', // Relative URL - will be normalized to absolute
  image: 'https://example.com/profile.jpg', // Absolute URL - used as-is
  knowsAbout: ['JavaScript', 'TypeScript'],
  renderHtml: true,
})
</script>

Generated HTML:

example.html
<div itemscope itemtype="https://schema.org/Person">
  <span itemprop="name">John Doe</span>
  <span itemprop="alternateName">JD</span>
  <span itemprop="jobTitle">Software Engineer</span>
  <a itemprop="url" href="https://example.com">https://example.com</a>
  <span itemprop="knowsAbout">JavaScript</span>
  <span itemprop="knowsAbout">TypeScript</span>
</div>

Organization

pages/example.vue
<script setup lang="ts">
useSchema({
  context: 'https://schema.org',
  type: 'Organization',
  name: 'My Company',
  description: 'Company description',
  url: '/', // Relative URL - will be normalized to absolute
  logo: '/images/logo.png', // Relative URL - will be normalized to absolute
  renderHtml: true,
})
</script>

Generated HTML:

example.html
<div itemscope itemtype="https://schema.org/Organization">
  <span itemprop="name">My Company</span>
  <span itemprop="description">Company description</span>
  <a itemprop="url" href="https://example.com">https://example.com</a>
</div>

ItemList

pages/example.vue
<script setup lang="ts">
useSchema({
  context: 'https://schema.org',
  type: 'ItemList',
  name: 'Top 10 Languages',
  description: 'List description',
  itemListElement: [
    {
      type: 'ListItem',
      position: 1,
      name: 'JavaScript',
      item: '/tech/javascript', // Relative URL - will be normalized to absolute
    },
    {
      type: 'ListItem',
      position: 2,
      name: 'TypeScript',
      item: 'https://www.typescriptlang.org', // Absolute URL - used as-is
    },
  ],
  renderHtml: true,
})
</script>

Generated HTML:

example.html
<div itemscope itemtype="https://schema.org/ItemList">
  <span itemprop="name">Top 10 Languages</span>
  <span itemprop="description">List description</span>
  <ol>
    <li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
      <meta itemprop="position" content="1">
      <span itemprop="name">JavaScript</span>
      <a itemprop="item" href="https://example.com/javascript">https://example.com/javascript</a>
    </li>
  </ol>
</div>

Article

pages/example.vue
<script setup lang="ts">
useSchema({
  context: 'https://schema.org',
  type: 'Article',
  headline: 'Article Title',
  description: 'Article description',
  datePublished: '2024-01-15',
  image: '/images/article.jpg', // Relative URL - will be normalized to absolute
  author: {
    type: 'Person',
    name: 'John Doe',
    url: 'https://example.com/author', // Absolute URL - used as-is
  },
  renderHtml: true,
})
</script>

Generated HTML:

example.html
<div itemscope itemtype="https://schema.org/Article">
  <span itemprop="headline">Article Title</span>
  <span itemprop="description">Article description</span>
  <span itemprop="datePublished">2024-01-15</span>
  <div itemprop="author" itemscope itemtype="https://schema.org/Person">
    <span itemprop="name">John Doe</span>
  </div>
</div>

Verification

Check in Developer Tools

  1. Open Developer Tools (F12)
  2. Check inside the <body> tag in the Elements tab
  3. Find elements with the nuxt-aeo-semantic-* class

Check in Console

Run the following command in the Developer Tools console:

example.html
// Check semantic HTML
document.querySelectorAll('[class*="nuxt-aeo-semantic"]').forEach(el => {
  console.log('✅ Found:', el.className, el.innerHTML.substring(0, 100) + '...')
})

// Check JSON-LD schema
document.querySelectorAll('script[type="application/ld+json"]').forEach(script => {
  console.log('✅ JSON-LD:', JSON.parse(script.innerHTML))
})

Why Is Semantic HTML Needed?

JSON-LD Alone May Not Be Enough

Some LLM crawlers and search engines may not be sufficient with JSON-LD alone. Using semantic HTML together:

  • Better Crawling: Crawlers that read HTML source directly can better understand content
  • Double Assurance: Using JSON-LD and semantic HTML together ensures maximum compatibility
  • AI Model Optimization: AI models like ChatGPT, Claude, and Perplexity can understand content more accurately

No Visual Impact

Important: The generated semantic HTML is hidden with the visually-hidden class and is not visible to users. However, it is included in the HTML source so LLMs and crawlers can read it.



Frequently Asked Questions

This page includes FAQPage Schema to help AI models and search engines understand common questions about automatic semantic HTML generation.

pages/example.vue
<script setup lang="ts">
useSchemaFaq({
  mainEntity: [
    {
      name: 'Why is semantic HTML needed?',
      acceptedAnswer: {
        text: 'Some LLM crawlers and search engines may not be sufficient with JSON-LD alone. Using JSON-LD and semantic HTML together allows crawlers that read HTML source directly to better understand content and ensures maximum compatibility.',
      },
    },
    {
      name: 'What is the default value of the renderHtml option?',
      acceptedAnswer: {
        text: 'For useSchemaFaq(), the default value of renderHtml is true, while for useSchema(), the default is false. In global configuration, the default value of renderHtml is also true.',
      },
    },
    {
      name: 'Is the generated HTML visible to users?',
      acceptedAnswer: {
        text: 'No, the generated semantic HTML is hidden with the visually-hidden class and is not visible to users. However, it is included in the HTML source so LLM crawlers and search engines can read it.',
      },
    },
    {
      name: 'Which Schema types generate semantic HTML?',
      acceptedAnswer: {
        text: 'Semantic HTML is automatically generated for major Schema types such as Person, Organization, ItemList, Article, and FAQPage. Appropriate HTML structure and microdata attributes are generated for each type.',
      },
    },
  ],
})
</script>

Question List

  • Why is semantic HTML needed?: JSON-LD alone may not be sufficient, so using semantic HTML together helps crawlers better understand content
  • What is the default value of the renderHtml option?: The default is true for useSchemaFaq() and false for useSchema()
  • Is the generated HTML visible to users?: No, it is hidden with the visually-hidden class and not visible, but it is included in the HTML source
  • Which Schema types generate semantic HTML?: Generated for major types such as Person, Organization, ItemList, Article, and FAQPage

Next Steps