npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

seo-manager-pro

v26.6.20

Published

SEO + AEO manager for Angular, React, Vue and Vanilla JS — search engines and AI answer engines.

Readme

SEO Manager Pro

NPM Version

A lightweight, framework-agnostic SEO + AEO manager for Angular, React, Vue, and Vanilla JS SPAs.

The package is split into two independent modules:

| Module | Class | Purpose | |--------|-------|---------| | SEO | SeoManager | Google, Bing, social previews (Open Graph, Twitter, canonical, robots) | | AEO | AeoManager | AI answer engines (ChatGPT, Claude, Perplexity) — llms.txt, page summaries, AI robots, FAQ/HowTo schema |

Use them together or separately. Each has its own reset() method and does not interfere with the other.


Features

| Category | What you get | |----------|--------------| | Basic Meta | title, description, keywords, author, theme-color, language (<html lang>) | | Open Graph | og:title, og:description, og:image, image alt/size, og:url, og:type, og:site_name, og:locale | | Article OG | article:published_time, article:modified_time, article:author, article:section, article:tag | | Twitter Cards | twitter:card, twitter:site, twitter:creator, title, description, image | | Indexing | robots + googlebot, canonical URL | | International | hreflang alternate links | | Pagination / Feeds | prev, next, alternate, manifest, sitemap link tags | | Structured Data | Built-in Schema.org types + raw jsonLd for anything else | | Breadcrumbs | Auto-generates BreadcrumbList JSON-LD from a simple array | | Icons | Dynamic favicon and apple-touch-icon | | Cleanup | resetSeo() removes everything the library injected | | SSR-safe | No-op when document is unavailable (Node / SSR) |

AEO (Answer Engine Optimization)

| Category | What you get | |----------|--------------| | Page Summary | Machine-readable JSON + abstract / summary meta for AI parsers | | AI Crawlers | Per-bot meta hints (GPTBot, ClaudeBot, PerplexityBot, …) | | llms.txt | generateLlmsTxt() + discoverable link tag | | Markdown alt | Link to Markdown version of the page | | FAQ / HowTo | Auto FAQPage and HowTo JSON-LD | | Entities | @graph entity markup with sameAs for AI citation | | Citation | Author, dates, license, publisher for AI attribution | | Speakable | SpeakableSpecification for voice assistants | | Presets | AeoPresets.article(), .product(), .faq(), .howTo() | | Cleanup | resetAeo() removes all AEO-injected tags |


Installation

npm install seo-manager-pro
yarn add seo-manager-pro

Quick Start

import { SeoManager, AeoManager, AeoPresets } from 'seo-manager-pro';

// Classic SEO (search engines + social)
SeoManager.updateSeo({
  title: 'Home Page | My Shop',
  description: 'Welcome to the best online store.',
  image: 'https://example.com/og-image.jpg',
  canonicalUrl: 'https://example.com/',
  robots: 'index,follow',
  openGraph: { siteName: 'My Shop', locale: 'en_US' },
  twitter: { card: 'summary_large_image', site: '@myshop' },
});

// AEO (AI answer engines)
AeoManager.updateAeo({
  llmsTxtUrl: 'https://example.com/llms.txt',
  pageSummary: {
    title: 'My Shop',
    description: 'Online store for electronics and accessories.',
    url: 'https://example.com/',
    keyPoints: ['Free shipping', '24/7 support', 'Secure checkout'],
    topics: ['ecommerce', 'electronics'],
  },
  aiRobots: {
    GPTBot: 'allow',
    ClaudeBot: 'allow',
    PerplexityBot: 'allow',
  },
});

// Or use an AEO preset (ideal for AI coding tools)
AeoManager.updateAeo(
  AeoPresets.article({
    title: '10 SEO Tips',
    description: 'Practical tips for SPA SEO.',
    url: 'https://example.com/blog/seo-tips',
    author: 'Jane Doe',
    publishedAt: '2025-06-01T10:00:00Z',
    keyPoints: ['Use canonical URLs', 'Add structured data'],
  })
);

SeoManagerPro is still exported as an alias for SeoManager (backward compatible).


API Reference

SEO — SeoManager.updateSeo(config: SeoConfig)

Updates all SEO tags for the current page. Previous SEO tags injected by this library are removed first.

SEO — SeoManager.resetSeo()

Removes every SEO tag/link/script the library added and clears document.title and <html lang>.

AEO — AeoManager.updateAeo(config: AeoConfig)

Injects AI-friendly metadata: page summary JSON, FAQ/HowTo schema, entity graphs, llms.txt link, and AI crawler hints.

AEO — AeoManager.resetAeo()

Removes all AEO-injected tags without touching SEO tags.

AEO — AeoManager.generateLlmsTxt(config: LlmsTxtConfig): string

Generates llms.txt markdown content. Host it at https://yoursite.com/llms.txt:

const content = AeoManager.generateLlmsTxt({
  siteName: 'My Shop',
  description: 'Online electronics store.',
  pages: [
    { title: 'Products', url: 'https://example.com/products', summary: 'All products' },
    { title: 'FAQ', url: 'https://example.com/faq', summary: 'Common questions' },
  ],
});
// Serve `content` as a static file at /llms.txt

AEO — AeoPresets

| Method | Use case | |--------|----------| | AeoPresets.article({...}) | Blog posts and articles | | AeoPresets.product({...}) | Product pages | | AeoPresets.faq({...}) | FAQ pages | | AeoPresets.howTo({...}) | Tutorials and guides |

Subpath imports

import { SeoManager } from 'seo-manager-pro/seo';
import { AeoManager, AeoPresets } from 'seo-manager-pro/aeo';

SeoConfig Options

Core fields

| Field | Type | Required | Description | |-------|------|----------|-------------| | title | string | ✅ | Page title (<title>) | | description | string | ✅ | Meta description | | image | string | | Default image for Open Graph and Twitter | | canonicalUrl | string | | Canonical URL (<link rel="canonical">) | | robots | RobotsDirective | | Crawler directive — also sets googlebot | | keywords | string \| string[] | | Meta keywords (comma-separated if array) | | author | string | | Content author | | themeColor | string | | Browser UI color (theme-color) | | language | string | | Sets <html lang="..."> (e.g. en, fa, de) | | favicon | string | | Favicon URL | | appleTouchIcon | string | | Apple touch icon URL |

robots values

'index,follow'    // default — allow indexing and following links
'noindex,nofollow' // hide page and don't follow links
'index,nofollow'   // index page but don't follow links
'noindex,follow'   // don't index but follow links

openGraph

| Field | Type | Description | |-------|------|-------------| | title | string | Overrides og:title (defaults to title) | | type | OpenGraphType | website, article, product, profile, book, video.other | | url | string | Overrides og:url (defaults to canonicalUrl) | | siteName | string | og:site_name | | locale | string | e.g. en_US, fa_IR | | imageAlt | string | og:image:alt | | imageWidth | number | og:image:width | | imageHeight | number | og:image:height | | publishedTime | string | ISO 8601 — article:published_time | | modifiedTime | string | ISO 8601 — article:modified_time | | author | string | article:author | | section | string | article:section | | tags | string[] | Multiple article:tag meta tags |

twitter

| Field | Type | Description | |-------|------|-------------| | card | TwitterCardType | summary, summary_large_image, app, player | | site | string | @username of the site | | creator | string | @username of the content creator | | title | string | Overrides page title for Twitter | | description | string | Overrides description for Twitter | | image | string | Overrides image for Twitter |

If twitter.card is omitted, summary_large_image is used when image is set; otherwise summary.

hreflang

For multilingual / regional SEO:

hreflang: [
  { lang: 'en', url: 'https://example.com/en/page' },
  { lang: 'fa', url: 'https://example.com/fa/page' },
  { lang: 'x-default', url: 'https://example.com/page' },
]

alternateLinks

Pagination, feeds, manifest, and other <link rel="..."> tags:

alternateLinks: [
  { rel: 'prev', href: 'https://example.com/blog?page=1' },
  { rel: 'next', href: 'https://example.com/blog?page=3' },
  { rel: 'sitemap', href: 'https://example.com/sitemap.xml', type: 'application/xml' },
]

breadcrumbs

Automatically builds a BreadcrumbList Schema.org script:

breadcrumbs: [
  { name: 'Home', url: 'https://example.com/' },
  { name: 'Blog', url: 'https://example.com/blog' },
  { name: 'Post Title', url: 'https://example.com/blog/post' },
]

schema

Inject typed Schema.org JSON-LD:

schema: [
  {
    type: 'Product',
    data: {
      name: 'iPhone 15',
      description: 'The best smartphone ever.',
      image: 'https://example.com/iphone.jpg',
      offers: {
        '@type': 'Offer',
        price: 999,
        priceCurrency: 'USD',
        availability: 'https://schema.org/InStock',
      },
    },
  },
  {
    type: 'FAQPage',
    data: {
      mainEntity: [
        {
          '@type': 'Question',
          name: 'Is it waterproof?',
          acceptedAnswer: {
            '@type': 'Answer',
            text: 'Yes, it has IP68 rating.',
          },
        },
      ],
    },
  },
]

jsonLd

For schemas not covered by built-in types, pass raw JSON-LD objects:

jsonLd: [
  {
    '@context': 'https://schema.org',
    '@type': 'HowTo',
    name: 'How to install SEO Manager Pro',
    step: [
      { '@type': 'HowToStep', text: 'Run npm install seo-manager-pro' },
      { '@type': 'HowToStep', text: 'Call SeoManagerPro.updateSeo(...)' },
    ],
  },
]

customMetaTags

Any extra <meta name="..." content="..."> tags:

customMetaTags: [
  { name: 'referrer', content: 'strict-origin-when-cross-origin' },
  { name: 'format-detection', content: 'telephone=no' },
]

AeoConfig Options

| Field | Type | Description | |-------|------|-------------| | pageSummary | PageSummary | Title, description, key points, topics, entities — injected as JSON | | aiRobots | Partial<Record<AiCrawlerBot, AiCrawlerPolicy>> | Per-bot policy: GPTBot, ClaudeBot, PerplexityBot, … | | llmsTxtUrl | string | URL to /llms.txt (adds discoverable link tag) | | markdownUrl | string | Alternate Markdown version of the page | | faq | FaqItem[] | Auto-builds FAQPage JSON-LD | | howTo | HowToConfig | Auto-builds HowTo JSON-LD | | speakable | SpeakableConfig | CSS/XPath selectors for voice assistants | | citation | CitationConfig | Author, dates, license for AI attribution | | entities | EntityReference[] | Named entities with optional sameAs URLs | | schema | AeoSchemaConfig[] | Typed AI-oriented Schema.org blocks | | jsonLd | Record<string, unknown>[] | Raw JSON-LD | | jsonLdGraph | Record<string, unknown>[] | Multiple nodes in one @graph | | customAiMetaTags | { name, content }[] | Extra AI-oriented meta tags |

Supported AI crawlers (aiRobots)

GPTBot, ChatGPT-User, ClaudeBot, anthropic-ai, Google-Extended, PerplexityBot, Bytespider, Applebot-Extended, cohere-ai, CCBot

Full AEO example

AeoManager.updateAeo({
  llmsTxtUrl: 'https://example.com/llms.txt',
  markdownUrl: 'https://example.com/blog/seo-tips.md',
  pageSummary: {
    title: '10 SEO Tips for SPAs',
    description: 'A practical guide to SPA SEO and AEO.',
    url: 'https://example.com/blog/seo-tips',
    language: 'en',
    keyPoints: [
      'Set canonical URLs on every route',
      'Add FAQ schema for AI snippets',
      'Publish llms.txt for agent discovery',
    ],
    topics: ['seo', 'spa', 'aeo'],
    entities: [
      { name: 'Google', type: 'Organization', sameAs: ['https://www.google.com'] },
    ],
  },
  aiRobots: {
    GPTBot: 'allow',
    ClaudeBot: 'allow',
    'Google-Extended': 'disallow',
  },
  citation: {
    author: 'Jane Doe',
    publishedAt: '2025-06-01T10:00:00Z',
    license: 'https://creativecommons.org/licenses/by/4.0/',
  },
  faq: [
    { question: 'What is AEO?', answer: 'Answer Engine Optimization for AI systems.' },
  ],
  jsonLdGraph: [
    { '@type': 'WebSite', name: 'My Blog', url: 'https://example.com' },
    { '@type': 'WebPage', name: '10 SEO Tips', url: 'https://example.com/blog/seo-tips' },
  ],
});

Supported Schema Types

| Type | Best for | |------|----------| | Product | E-commerce product pages | | FAQPage | FAQ sections | | Article | Generic articles | | BlogPosting | Blog posts | | Organization | Company / brand pages | | WebSite | Site-wide search box, publisher info | | WebPage | Generic landing pages | | LocalBusiness | Local SEO (address, hours, geo) | | Person | Author / profile pages | | BreadcrumbList | Manual breadcrumb schema (or use breadcrumbs helper) | | Event | Events, webinars, concerts | | VideoObject | Video pages | | Recipe | Recipe pages | | SoftwareApplication | Apps, SaaS products |


Framework Examples

React (with useEffect)

import { useEffect } from 'react';
import { SeoManagerPro } from 'seo-manager-pro';

function ProductPage({ product }) {
  useEffect(() => {
    SeoManagerPro.updateSeo({
      title: `${product.name} | My Shop`,
      description: product.summary,
      image: product.image,
      canonicalUrl: `https://example.com/products/${product.slug}`,
      robots: 'index,follow',
      openGraph: { type: 'product' },
      schema: [
        {
          type: 'Product',
          data: {
            name: product.name,
            image: product.image,
            offers: {
              '@type': 'Offer',
              price: product.price,
              priceCurrency: 'USD',
            },
          },
        },
      ],
    });

    return () => SeoManagerPro.resetSeo();
  }, [product]);

  return <div>{product.name}</div>;
}

Vue 3 (onMounted / watch)

import { watch, onUnmounted } from 'vue';
import { SeoManagerPro } from 'seo-manager-pro';

watch(
  () => route.fullPath,
  () => {
    SeoManagerPro.updateSeo({
      title: 'My Vue App',
      description: 'Built with Vue 3',
      canonicalUrl: `https://example.com${route.fullPath}`,
    });
  },
  { immediate: true }
);

onUnmounted(() => SeoManagerPro.resetSeo());

Angular (service or resolver)

import { Injectable } from '@angular/core';
import { SeoManagerPro, SeoConfig } from 'seo-manager-pro';

@Injectable({ providedIn: 'root' })
export class SeoService {
  update(config: SeoConfig): void {
    SeoManagerPro.updateSeo(config);
  }

  reset(): void {
    SeoManagerPro.resetSeo();
  }
}

Use in a route resolver or ngOnInit / ngOnDestroy of your page component.

Vanilla JS

<script type="module">
  import { SeoManagerPro } from 'seo-manager-pro';

  SeoManagerPro.updateSeo({
    title: 'Contact Us',
    description: 'Get in touch with our team.',
    canonicalUrl: 'https://example.com/contact',
  });
</script>

Blog Post Example (full SEO)

SeoManagerPro.updateSeo({
  title: '10 SEO Tips for SPAs | My Blog',
  description: 'Learn how to optimize single-page apps for search engines.',
  image: 'https://example.com/blog/seo-tips/cover.jpg',
  canonicalUrl: 'https://example.com/blog/seo-tips',
  robots: 'index,follow',
  author: 'Jane Doe',
  language: 'en',
  openGraph: {
    type: 'article',
    siteName: 'My Blog',
    locale: 'en_US',
    publishedTime: '2025-06-01T10:00:00Z',
    modifiedTime: '2025-06-15T14:30:00Z',
    author: 'Jane Doe',
    section: 'SEO',
    tags: ['seo', 'spa', 'javascript'],
    imageAlt: 'SEO tips cover image',
  },
  twitter: {
    card: 'summary_large_image',
    site: '@myblog',
    creator: '@janedoe',
  },
  breadcrumbs: [
    { name: 'Home', url: 'https://example.com/' },
    { name: 'Blog', url: 'https://example.com/blog' },
    { name: '10 SEO Tips for SPAs', url: 'https://example.com/blog/seo-tips' },
  ],
  schema: [
    {
      type: 'BlogPosting',
      data: {
        headline: '10 SEO Tips for SPAs',
        author: { '@type': 'Person', name: 'Jane Doe' },
        datePublished: '2025-06-01T10:00:00Z',
        dateModified: '2025-06-15T14:30:00Z',
        image: 'https://example.com/blog/seo-tips/cover.jpg',
      },
    },
  ],
});

Local Business Example

SeoManagerPro.updateSeo({
  title: 'Coffee House Tehran | Best Coffee',
  description: 'Specialty coffee in downtown Tehran. Open daily 8 AM – 10 PM.',
  canonicalUrl: 'https://example.com/tehran-coffee',
  robots: 'index,follow',
  language: 'fa',
  openGraph: {
    type: 'website',
    locale: 'fa_IR',
    siteName: 'Coffee House',
  },
  schema: [
    {
      type: 'LocalBusiness',
      data: {
        name: 'Coffee House Tehran',
        address: {
          '@type': 'PostalAddress',
          streetAddress: 'Valiasr St',
          addressLocality: 'Tehran',
          addressCountry: 'IR',
        },
        geo: {
          '@type': 'GeoCoordinates',
          latitude: 35.6892,
          longitude: 51.3890,
        },
        openingHours: 'Mo-Su 08:00-22:00',
        telephone: '+98-21-12345678',
      },
    },
  ],
});

SSR Notes

The library checks for document before touching the DOM. During server-side rendering, updateSeo() and resetSeo() are safe no-ops.

For best SEO in SSR frameworks (Next.js, Nuxt, Angular SSR), prefer setting meta tags in server-rendered HTML or framework-specific head APIs. Use SEO Manager Pro on the client for SPA route transitions and dynamic pages.


Best Practices

  1. Call on every route change — SPAs don't reload the page; update SEO when navigation happens.
  2. Use resetSeo() on unmount — Avoid stale tags when leaving a page.
  3. Always set canonicalUrl — Prevents duplicate-content issues with query strings or trailing slashes.
  4. Use absolute URLs — For image, canonicalUrl, and schema URLs.
  5. Match OG and Twitter — The library auto-fills Twitter from title/description/image; override only when needed.
  6. Add hreflang for multilingual sites — Include x-default for the fallback locale.
  7. Prefer JSON-LD — Google recommends JSON-LD for structured data; this library injects it automatically.
  8. Validate structured data — Test with Google Rich Results Test.

TypeScript

All types are exported from the package:

import {
  SeoManager,
  SeoManagerPro,
  AeoManager,
  AeoPresets,
  SeoConfig,
  AeoConfig,
  SchemaType,
  OpenGraphConfig,
  TwitterConfig,
  PageSummary,
  LlmsTxtConfig,
} from 'seo-manager-pro';

Includes:

  • ✅ Angular Demo
  • ✅ React Demo
  • ✅ Vanilla JavaScript Demo

License

MIT © MohammadBagher Sharifi