@neuctra/ui-mcp 0.4.6 → 0.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/data/design-rules.json +337 -0
- package/data/layouts.json +3249 -765
- package/data/seo-guide.json +367 -0
- package/package.json +3 -3
- package/src/load-data.mjs +2 -2
- package/src/tools/get-design-rules/index.mjs +46 -24
- package/src/tools/neuctra-ui-page-builder/index.mjs +25 -14
- package/src/tools/neuctra-ui-seo-setup/index.mjs +59 -20
- package/data/aiDesignRules.json +0 -325
- package/data/seoGuide.json +0 -180
package/data/seoGuide.json
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"title": "Neuctra UI: SEO, AEO and GEO Guide",
|
|
3
|
-
"intro": "Complete SEO, AEO, and GEO rules for Neuctra UI projects, followed by practical implementation steps. Use the rules as the source of truth, identify the project framework before applying metadata, and adapt every implementation to that framework. The guidance covers crawlability, technical SEO, keywords, metadata, headings, content, structured data, FAQs, performance, trust, and measurement.",
|
|
4
|
-
"seoRules": [
|
|
5
|
-
{
|
|
6
|
-
"id": "technical",
|
|
7
|
-
"title": "Technical SEO Foundations",
|
|
8
|
-
"content": "Search engines and AI crawlers can only rank what they can fetch, render and understand.\n\n**Render on the server.** Titles, meta tags, JSON-LD and the main body text must be in the initial HTML (SSR or SSG). Google renders JavaScript late and not always; most AI crawlers (GPTBot, ClaudeBot, PerplexityBot) do not run JavaScript at all. A client-only SPA that patches `<title>` in a `useEffect` is effectively invisible to them.\n\n**Canonical URLs.** Every indexable page declares one absolute canonical: `<link rel=\"canonical\" href=\"https://example.com/docs/button\">`. Pick one host (with or without `www`), one protocol (`https`) and one trailing-slash style, and 301-redirect every other variant to it.\n\n**URLs.** Short, lowercase, kebab-case, readable: `/docs/date-picker`, not `/Docs/DatePicker?id=12`. Never change a live URL without a 301 redirect.\n\n**robots.txt.** Lives at `/robots.txt`, points to the sitemap, and blocks only what must never be crawled (admin, API, internal search results):\n\n```text\nUser-agent: *\nAllow: /\nDisallow: /api/\nDisallow: /admin/\n\nSitemap: https://example.com/sitemap.xml\n```\n\n`robots.txt` controls crawling, not indexing. To keep a crawlable page out of results use `<meta name=\"robots\" content=\"noindex\">` (and do not also block it in robots.txt, or the noindex is never seen).\n\n**XML sitemap.** Lists every canonical, indexable URL (never redirects, 404s or noindex pages) with an accurate `<lastmod>`. Regenerate it on every build (e.g. `next-sitemap`, `@astrojs/sitemap`) and submit it in Google Search Console and Bing Webmaster Tools.\n\n**Site architecture.** Every important page is reachable within 3 clicks of the home page and has at least one internal link pointing to it (no orphan pages). Use real `<a href>` links; buttons with `onClick` navigation are not crawled.\n\n**Status codes.** 200 for real pages, 301 for permanent moves, 404/410 for removed pages. A \"not found\" screen that returns 200 (soft 404) wastes crawl budget.\n\n**noindex** thin or utility pages: login, cart, account, internal search, tag pages with one post, paginated archives beyond page 1 if they add nothing.\n\n**International sites.** Use `hreflang` alternates (`<link rel=\"alternate\" hreflang=\"de\" href=\"...\">`) plus `x-default`, and keep one language per URL.\n\n**HTTPS and security.** HTTPS everywhere, no mixed content, HSTS enabled.\n\n**Only the widget may be client-only.** Interactive tools (editors, converters, canvas, file processing) often need `dynamic(() => import(\"./Tool\"), { ssr: false })` or a client-only island. Wrap only the widget that way. The page frame around it (breadcrumb, H1, lead paragraph, article sections, FAQ and JSON-LD) must render on the server. If the whole page is client-only, the built HTML has no H1, no text and no schema, and crawlers see an empty page.\n\n**One URL registry.** Keep every route in one data file (e.g. `lib/routes.js` with categories and pages, each with an `href`). Generate navigation, the sitemap, `llms.txt`, breadcrumbs and related links from it, so a renamed URL can't leave stale links behind.\n\n**Redirect old slugs.** When a URL changes, add a permanent 301 from the old path, update the registry, and regenerate the sitemap and `llms.txt`. They must list only final URLs, never URLs that redirect.\n\n**Sitemap dates.** Set `<lastmod>` to the date the content really changed, not the build time for every URL: a lastmod that always changes is ignored. Google ignores `<priority>` and `<changefreq>`, so don't spend effort tuning them. Exclude admin, API, 404 and other noindex routes."
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
"id": "performance",
|
|
12
|
-
"title": "Performance and Core Web Vitals",
|
|
13
|
-
"content": "Core Web Vitals are a ranking signal and a conversion factor. Targets at the 75th percentile of real users:\n\n```text\nLCP (Largest Contentful Paint) < 2.5s\nINP (Interaction to Next Paint) < 200ms\nCLS (Cumulative Layout Shift) < 0.1\n```\n\n**LCP**\n* The hero image is the usual LCP element: serve it in AVIF/WebP, sized to the viewport, and mark it high priority (`priority` on `next/image`, `fetchpriority=\"high\"` and no `loading=\"lazy\"` on a plain `<img>`).\n* Lazy-load everything below the fold.\n* Preconnect to critical third-party origins; self-host fonts where possible.\n\n**CLS**\n* Give every image, video and embed explicit `width`/`height` or an `aspect-*` class.\n* Reserve space for banners, ads and late-loading content.\n* Load fonts with `font-display: swap` and a size-matched fallback.\n\n**INP**\n* Keep main-thread tasks under 50ms: split large bundles, defer non-critical scripts, avoid heavy work in click handlers.\n* Load analytics and chat widgets after interaction or idle.\n\n**Measure** with PageSpeed Insights (field + lab data), the Search Console Core Web Vitals report and Lighthouse. Lab scores alone are not what Google ranks on.\n\n**Avoid layout shift from theme and ads**\n* Apply the saved light/dark theme with a tiny inline script in `<head>` before first paint, so the page doesn't flash and re-layout.\n* Reserve the height of ad slots and embeds before they load, and only load them when they near the viewport."
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"id": "on-page",
|
|
17
|
-
"title": "On-Page SEO",
|
|
18
|
-
"content": "**Title tag** (the single strongest on-page signal)\n* 50 to 60 characters (Google truncates around 580px).\n* Primary keyword first, then a benefit or differentiator, then the brand: `React Date Picker Component: Accessible and Themeable | Neuctra UI`, `Merge PDF Online Free: Combine Files, No Sign-Up | Brand`.\n* Long-tail modifiers people actually search belong in the title when true: \"free\", \"online\", \"no sign-up\", \"without watermark\", \"for React\".\n* **One brand-suffix rule for the whole site.** Add ` | Brand` in exactly one place (a title template or one metadata builder) and never hand-type it into individual titles. Mixing the two produces missing suffixes on some pages and doubled ones (`| Brand | Brand`) on others.\n* Unique on every page. Never \"Home\", \"Untitled\" or the same title site-wide.\n* Home page: `Brand: what it is in plain words`.\n\n**Meta description** (not a ranking factor, but it drives click-through)\n* 140 to 155 characters.\n* Formula: primary keyword + what the page or tool does + trust points + call to action. Example: \"Merge PDF files online for free. Combine documents in seconds, right in your browser. No upload, no sign-up, no watermark. Merge your PDFs now.\"\n* Trust points that are true for the page (\"no sign-up\", \"files never leave your device\", \"free forever\") lift click-through.\n* Unique per page. If missing or weak, Google writes its own from page text.\n\n**Headings**\n* Exactly one `<h1>`, matching the search intent and close to the title (not identical is fine).\n* `<h2>`/`<h3>` in order, never skipping levels. Phrase several `<h2>`s as the questions people actually search.\n\n**Body**\n* Primary keyword (or a close variant) in the first 100 words.\n* Cover the topic completely: answer the main question, the follow-up questions and the obvious objections.\n* Short paragraphs, lists and tables where they fit. Write for people; no keyword stuffing.\n\n**Links**\n* Descriptive internal anchor text (\"React table component\", never \"click here\").\n* Link down to child pages and across to related pages. Link out to authoritative sources when you cite facts.\n\n**Images**\n* Descriptive `alt` text for meaningful images, empty `alt=\"\"` for decorative ones.\n* Descriptive file names (`react-date-picker-dark-mode.webp`).\n\n**Social previews**\n* OpenGraph and Twitter tags with a 1200x630 image on every shareable page.\n\n**Social copy.** OpenGraph title and description may be a shorter, punchier variant of the SEO title and description (`og:title` does not need the brand suffix). Every page still needs its own `og:image`.\n\n**Meta keywords.** Google ignores `<meta name=\"keywords\">`. Research long-tail variants (free, online, no sign-up, without watermark, for beginners, vs competitor) and use them in the title, H2s, body and FAQ questions instead. A keywords tag is harmless, but it does nothing for ranking."
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
"id": "keywords-intent",
|
|
22
|
-
"title": "Keywords, Search Intent and Site Structure",
|
|
23
|
-
"content": "**One page, one primary intent.** Decide what a page should rank for before writing it:\n\n```text\nInformational \"what is a date picker\" guide, docs, blog\nCommercial \"best react ui library\" comparison, landing\nTransactional \"neuctra ui pricing\" pricing, signup\nNavigational \"neuctra ui docs\" home, docs index\n```\n\n* Pick one primary keyword and a handful of close variants per page.\n* Two pages targeting the same keyword compete with each other (cannibalisation). Merge them or differentiate the intent.\n\n**Topic clusters.** Build a pillar page for each core topic (e.g. \"React UI Components\") and cluster pages for each subtopic (Button, Modal, Date Picker). The pillar links to every cluster page and every cluster page links back to the pillar and to its siblings. This is how topical authority is earned.\n\n**Research sources.** Google Search Console queries you already rank for on page 2, \"People also ask\", autocomplete, related searches, competitor pages that rank, and your own support and sales questions.\n\n**Map it.** Keep a simple keyword map (URL, primary keyword, intent, status) so no two pages chase the same query."
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
"id": "eeat",
|
|
27
|
-
"title": "E-E-A-T and Trust Signals",
|
|
28
|
-
"content": "Google's quality raters look for Experience, Expertise, Authoritativeness and Trust. AI engines prefer the same sources when choosing what to cite.\n\n* **Real authorship.** Named authors with a short bio and links to their profiles for articles and guides. Use `author` in the schema.\n* **Dates.** Show \"Published\" and \"Last updated\" dates on content that ages, and mirror them in `datePublished` / `dateModified`. Only bump `dateModified` when the content really changed.\n* **Evidence.** Original data, benchmarks, screenshots, code that runs, and citations to primary sources.\n* **Transparency.** About, Contact, Privacy Policy and Terms pages; a physical or company address where relevant.\n* **Consistency.** The same brand name, logo and description everywhere (site, GitHub, npm, social profiles, directories).\n* **Reputation.** Reviews and mentions on third-party sites count for more than claims on your own site."
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"id": "aeo",
|
|
32
|
-
"title": "AEO: Answer Engine Optimisation",
|
|
33
|
-
"content": "AEO targets the answer surfaces: featured snippets, \"People also ask\", Google AI Overviews, voice assistants. The goal is to be the passage an engine lifts as the answer.\n\n**Answer-first structure**\n* Phrase key `<h2>`s as questions: \"How do I add a date picker in React?\"\n* Put a direct, self-contained answer of 40 to 60 words right under the question, then expand with detail.\n* Start definitions with the term: \"A date picker is a form control that ...\"\n\n**Snippet-friendly formats**\n* Steps: an ordered list (`<ol>`) with one action per item.\n* Comparisons: a real `<table>` with clear column headers.\n* Lists of options: an unordered list with the item name first.\n\n**FAQ block on every key page**\n* 4 to 8 real questions with concise answers (see the `faq` topic), marked up with `FAQPage` schema.\n\n**Voice and conversational queries**\n* Cover long, natural questions (\"which react ui library works with tailwind v4\").\n* Keep answers short and speakable; avoid tables-only answers for questions people ask aloud.\n\n**Make the answer easy to extract**\n* Plain HTML text, not text baked into images, canvas or tabs that only render on click.\n* One idea per paragraph. No filler introduction before the answer."
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"id": "geo",
|
|
37
|
-
"title": "GEO: Generative Engine Optimisation",
|
|
38
|
-
"content": "GEO targets being retrieved and cited by generative engines: ChatGPT search, Perplexity, Claude, Gemini, Copilot and Google AI Mode. They pick sources that are crawlable, clear, specific and trusted.\n\n**1. Let them crawl.** Server-render the content (they rarely run JavaScript) and make an explicit robots.txt decision per bot:\n\n```text\n# Search / answer bots: allow these if you want to be cited\nUser-agent: OAI-SearchBot\nAllow: /\nUser-agent: ChatGPT-User\nAllow: /\nUser-agent: PerplexityBot\nAllow: /\nUser-agent: Claude-SearchBot\nAllow: /\nUser-agent: Claude-User\nAllow: /\n\n# Training bots: a business decision, allow or disallow\nUser-agent: GPTBot\nAllow: /\nUser-agent: ClaudeBot\nAllow: /\nUser-agent: Google-Extended\nAllow: /\nUser-agent: CCBot\nAllow: /\n```\n\nBlocking a search bot means that engine cannot cite you. Blocking a training bot does not stop citations from its search bot.\n\n**2. Publish llms.txt.** A markdown index at `/llms.txt` (spec: llmstxt.org): an H1 with the site name, a one-paragraph summary in a blockquote, then sections of `- [Page title](url): one-line description`. Optionally `/llms-full.txt` with the full text of key docs. Generate it at build time from the same route registry as the sitemap, so it never lists old URLs that now redirect.\n\n**3. Be the clearest source.**\n* State facts plainly and specifically: numbers, versions, dates, limits, prices.\n* Keep sections self-contained so a single passage makes sense when quoted alone.\n* Add comparison tables, definitions and step lists; engines quote these heavily.\n* Cite primary sources and include original data or benchmarks worth citing.\n\n**4. Strengthen the entity.**\n* Use the same brand name and one-line description everywhere.\n* `Organization` schema with `logo` and `sameAs` links (GitHub, npm, LinkedIn, X, YouTube, Wikipedia/Wikidata if you have them).\n* Earn mentions on sites the engines already trust: docs aggregators, GitHub, Reddit, Stack Overflow, industry publications, review sites.\n\n**5. Stay fresh.** Show and maintain \"Last updated\" dates; engines favour current sources for anything version-sensitive.\n\n**6. Get indexed by Bing.** ChatGPT search and Copilot lean on Bing's index: verify the site in Bing Webmaster Tools, submit the sitemap and ping IndexNow on publish.\n\n**7. Measure.** Track referral traffic from chatgpt.com, perplexity.ai, gemini.google.com and copilot.microsoft.com, and regularly ask those engines your target questions to see who they cite."
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"id": "schema",
|
|
42
|
-
"title": "Structured Data and Schema Weaving",
|
|
43
|
-
"content": "Structured data (JSON-LD) tells engines exactly what a page is. **Schema weaving** means emitting one connected `@graph` per page instead of several disconnected blobs, with nodes referencing each other by stable `@id`.\n\n**Stable @id scheme** (reuse the exact same IDs on every page):\n\n```text\nOrganization https://example.com/#organization\nWebSite https://example.com/#website\nWebPage https://example.com/docs/card#webpage\nBreadcrumbList https://example.com/docs/card#breadcrumb\nMain entity https://example.com/docs/card#article (or #product, #software)\nFAQPage https://example.com/docs/card#faq\n```\n\n**Every page** gets Organization + WebSite + a WebPage (or subtype) + BreadcrumbList, wired together:\n* `WebSite.publisher` -> Organization\n* `WebPage.isPartOf` -> WebSite, `WebPage.breadcrumb` -> BreadcrumbList\n* main entity `mainEntityOfPage` -> WebPage, `publisher` -> Organization\n\n**Type map**\n\n```text\nhome WebPage (+ Organization, WebSite as the main entities)\ndocs WebPage + TechArticle\nblog WebPage + BlogPosting (author, datePublished, dateModified, image)\nproduct WebPage + SoftwareApplication or Product\npricing WebPage + Product with Offer items (real prices only)\nlanding WebPage\nfaq FAQPage (as the page type)\nabout AboutPage\ncontact ContactPage\ncategory CollectionPage\n```\n\n**Example graph**\n\n```json\n{\n \"@context\": \"https://schema.org\",\n \"@graph\": [\n { \"@type\": \"Organization\", \"@id\": \"https://example.com/#organization\", \"name\": \"Example\", \"url\": \"https://example.com\", \"logo\": \"https://example.com/logo.png\", \"sameAs\": [\"https://github.com/example\"] },\n { \"@type\": \"WebSite\", \"@id\": \"https://example.com/#website\", \"url\": \"https://example.com\", \"name\": \"Example\", \"publisher\": { \"@id\": \"https://example.com/#organization\" } },\n { \"@type\": \"WebPage\", \"@id\": \"https://example.com/docs/card#webpage\", \"url\": \"https://example.com/docs/card\", \"name\": \"Card Component | Example\", \"isPartOf\": { \"@id\": \"https://example.com/#website\" }, \"breadcrumb\": { \"@id\": \"https://example.com/docs/card#breadcrumb\" } },\n { \"@type\": \"BreadcrumbList\", \"@id\": \"https://example.com/docs/card#breadcrumb\", \"itemListElement\": [\n { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https://example.com/\" },\n { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"Docs\", \"item\": \"https://example.com/docs\" },\n { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Card\", \"item\": \"https://example.com/docs/card\" }\n ] },\n { \"@type\": \"TechArticle\", \"@id\": \"https://example.com/docs/card#article\", \"headline\": \"Card Component\", \"mainEntityOfPage\": { \"@id\": \"https://example.com/docs/card#webpage\" }, \"publisher\": { \"@id\": \"https://example.com/#organization\" } }\n ]\n}\n```\n\n**Rules**\n* Structured data must describe what is visible on the page. Never mark up content users cannot see.\n* Never invent reviews, ratings, prices or authors. Fake markup risks a manual action.\n* Since 2023 Google shows FAQ rich results only for well-known government and health sites, and HowTo rich results are gone. Still add `FAQPage` where there is a visible FAQ: it helps AI Overviews and generative engines understand the Q&A.\n* Emit JSON-LD in the server-rendered HTML. Escape `<` as `\\u003c` when injecting it into a `<script>` tag.\n* Validate every template with Google's Rich Results Test and validator.schema.org before shipping.\n\nFollow the implementation steps from `neuctra-ui-seo-setup` when adding a woven graph to a page.\n\n**One node per type per page.** A page must not emit two `SoftwareApplication`, two `FAQPage` or two `BreadcrumbList` nodes. This happens when a shared page shell emits schema and the individual page adds its own `<script type=\"application/ld+json\">` too. Pick one owner (the shell), pass the data into it, and delete the page-level duplicate.\n\n**One data source for visible content and schema.** Build the visible steps list and the `HowTo` node from the same array, and the visible FAQ accordion and the `FAQPage` node from the same array. They then can't drift apart, and nothing is shown twice.\n\n**Tool and web-app pages** (free online tools, converters, calculators, generators):\n* `SoftwareApplication` with `applicationCategory` (e.g. `UtilitiesApplication`, `DeveloperApplication`, `MultimediaApplication`), `operatingSystem` (\"Web\" or \"All\"), `featureList`, `isAccessibleForFree: true` and an `Offer` with `price: \"0\"` only if the tool really is free.\n* `HowTo` with the visible usage steps. Google no longer shows HowTo rich results, but engines still use the steps for answers.\n* `BreadcrumbList` (Home > Category > Tool) and `FAQPage`.\n\n**Listing pages**\n* Category or collection pages: `CollectionPage` + an `ItemList` of the pages it links to, in visible order.\n* Home page: `ItemList`s for the main categories and popular items, next to Organization and WebSite.\n\n**Optional extras, with honest expectations**\n* `WebSite` `potentialAction` `SearchAction`: fine if the site has search, but Google retired the sitelinks search box in 2024, so it no longer changes how results look.\n* `speakable`: a beta mostly limited to news publishers; low value for other sites."
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"id": "faq",
|
|
47
|
-
"title": "Writing FAQs That Rank and Get Cited",
|
|
48
|
-
"content": "**Where the questions come from:** Google \"People also ask\", autocomplete, Search Console queries, Reddit and forum threads, support tickets and sales calls. Never invent questions nobody asks.\n\n**Per page**\n* 4 to 8 questions, specific to that page. Never reuse the same FAQ block across pages.\n* Question as the user would type or say it: \"Does the date picker support keyboard navigation?\"\n* Answer in 40 to 80 words: direct answer in the first sentence, then the key detail. Plain text, no marketing fluff.\n* Include the page's primary keyword or entity name naturally in at least one question.\n\n**Markup**\n* Render the FAQ visibly on the page (an Accordion is fine, as long as the text is in the HTML).\n* Mirror the exact same questions and answers in `FAQPage` schema (`mainEntity` of `Question` items with an `acceptedAnswer` `Answer`).\n* The visible text and the schema text must match word for word.\n\n**Maintenance**\n* Update answers when the product changes, and bump `dateModified`.\n* Add new questions as support and search data reveal them."
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
"id": "meta-checklist",
|
|
52
|
-
"title": "Per-Page Meta Checklist",
|
|
53
|
-
"content": "Every indexable page ships these tags in the server-rendered `<head>`:\n\n```html\n<html lang=\"en\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Primary Keyword: Qualifier | Brand</title>\n<meta name=\"description\" content=\"140 to 155 characters, unique, with the value and a reason to click.\">\n<link rel=\"canonical\" href=\"https://example.com/path\">\n<meta name=\"robots\" content=\"index, follow, max-image-preview:large\">\n\n<meta property=\"og:type\" content=\"website\"> <!-- \"article\" for blog posts -->\n<meta property=\"og:site_name\" content=\"Brand\">\n<meta property=\"og:title\" content=\"Same as or close to the title\">\n<meta property=\"og:description\" content=\"Same as or close to the description\">\n<meta property=\"og:url\" content=\"https://example.com/path\">\n<meta property=\"og:image\" content=\"https://example.com/og/path.png\"> <!-- 1200x630 -->\n<meta property=\"og:image:alt\" content=\"What the image shows\">\n\n<meta name=\"twitter:card\" content=\"summary_large_image\">\n<meta name=\"twitter:title\" content=\"...\">\n<meta name=\"twitter:description\" content=\"...\">\n<meta name=\"twitter:image\" content=\"https://example.com/og/path.png\">\n\n<link rel=\"icon\" href=\"/favicon.svg\" type=\"image/svg+xml\">\n<link rel=\"apple-touch-icon\" href=\"/apple-touch-icon.png\">\n<meta name=\"theme-color\" content=\"#ffffff\">\n\n<script type=\"application/ld+json\">{ \"@context\": \"https://schema.org\", \"@graph\": [ ... ] }</script>\n```\n\nChecks:\n* Title and description are unique across the whole site.\n* Canonical and `og:url` are absolute and identical.\n* OG image URL is absolute, 1200x630, under about 5MB, and readable at small sizes.\n* Blog posts also get `article:published_time` and `article:modified_time`.\n* Utility pages use `<meta name=\"robots\" content=\"noindex, follow\">`."
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
"id": "frameworks",
|
|
57
|
-
"title": "Where Metadata Goes in Each Framework",
|
|
58
|
-
"content": "Neuctra UI runs anywhere React 18+ and Tailwind v4 run. The rule is the same everywhere: metadata and JSON-LD must be in the server-rendered HTML. Use the implementation steps from `neuctra-ui-seo-setup` for the matching framework; do not apply metadata APIs from another framework.\n\n**Next.js App Router** (`framework: \"nextjs-app\"`)\n* `export const metadata = {...}` or `export async function generateMetadata()` in `page.js` or `layout.js`. Use `alternates.canonical`, `openGraph`, `twitter`, `robots`.\n* Set `metadataBase: new URL(\"https://example.com\")` in the root layout so relative URLs resolve.\n* A `\"use client\"` page cannot export metadata: put it in the route's `layout.js` instead.\n* **Metadata merging is shallow.** A child segment's `openGraph` object replaces the parent's completely. If a page sets `openGraph` at all, it must repeat `images` (and `siteName`, `locale`), or the page ships without an `og:image`. The same applies to `twitter`.\n* **Title template.** `title: { template: \"%s | Brand\", default: \"...\" }` in the root layout adds the suffix to child titles. Child titles must then not include the brand themselves, or it doubles. When a builder already returns a full title, use `title: { absolute: \"...\" }` so the template is skipped.\n* **Robots for Googlebot** uses kebab-case keys: `robots: { index: true, follow: true, googleBot: { index: true, follow: true, \"max-image-preview\": \"large\", \"max-snippet\": -1, \"max-video-preview\": -1 } }`. camelCase keys like `maxImagePreview` are ignored.\n* **Client-only widgets:** `dynamic(() => import(\"./Widget\"), { ssr: false })` must wrap only the widget. Keep the H1, text, FAQ and JSON-LD in the server-rendered `page.js`.\n* `output: \"export\"` (static export) is fully crawlable: everything above still applies, and you can check the result in the `out/` HTML.\n* JSON-LD: render `<script type=\"application/ld+json\" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, \"\\\\u003c\") }} />` in the page or layout.\n* `app/sitemap.js` and `app/robots.js` generate those files natively.\n\n**Next.js Pages Router** (`\"nextjs-pages\"`)\n* `import Head from \"next/head\"` and render the tags inside `<Head>`. Pages are pre-rendered, so crawlers see them.\n\n**Vite + React SPA** (`\"react-vite\"`)\n* React 19 hoists `<title>`, `<meta>` and `<link>` rendered anywhere in the tree into `<head>`. On React 18 use `react-helmet-async`.\n* Warning: a client-only SPA ships an empty HTML shell. Prerender the routes at build time or move to an SSR framework (Next.js, React Router, Astro, TanStack Start) for anything that must rank or be cited.\n\n**React Router v7 / Remix** (`\"react-router\"`)\n* Export `meta()` from the route module returning `[{ title }, { name: \"description\", content }, { tagName: \"link\", rel: \"canonical\", href }, { \"script:ld+json\": jsonLd }]`.\n\n**Astro** (`\"astro\"`)\n* Put the tags in the layout's `<head>`, passing title/description as props. JSON-LD: `<script type=\"application/ld+json\" set:html={JSON.stringify(jsonLd)} />`. Use React islands for Neuctra UI components. `@astrojs/sitemap` builds the sitemap.\n\n**TanStack Start** (`\"tanstack-start\"`)\n* Return `{ meta, links, scripts }` from the route's `head()` option; JSON-LD goes in `scripts` as `{ type: \"application/ld+json\", children: JSON.stringify(jsonLd) }`.\n\n**Gatsby** (`\"gatsby\"`)\n* `export const Head = () => (<>...tags...</>)` from the page component.\n\n**Plain HTML / other** (`\"html\"`)\n* Put the tags from the `meta-checklist` topic directly in `<head>`."
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
"id": "page-templates",
|
|
62
|
-
"title": "Page Templates for Tool, Category and Home Pages",
|
|
63
|
-
"content": "Consistent templates make every page crawlable, answerable and internally linked by default.\n\n**Tool / product page** (e.g. a free online tool or a single feature):\n\n```text\n<main>\n <header>\n Visible breadcrumb Home > Category > Tool (matches BreadcrumbList)\n Category Badge\n H1 primary keyword + differentiator\n Lead paragraph text-secondary, 1 to 2 sentences, keyword in it\n </header>\n The tool / product UI client-only widget is fine here\n <article>\n <section> H2 intro what it is, who it's for, keyword in first 100 words\n <section> H2 \"How to <task>\" <ol> steps (same data as the HowTo schema)\n <section> H2 benefits H3 per benefit\n <section> H2 \"Who uses <tool>\" use cases + 3 to 4 related links\n <section> H2 privacy / trust e.g. \"Your files stay on your device\"\n <section> H2 FAQ accordion, 6 to 8 questions (same data as FAQPage)\n </article>\n</main>\n```\n\n**Category page**\n* H1 + lead paragraph, then an H2 above the grid of items (never jump from H1 to H3 card titles).\n* Each card links to its page with descriptive anchor text.\n* `CollectionPage` + `ItemList` + `BreadcrumbList` schema.\n\n**Home page**\n* H1 stating what the site is, a lead paragraph, the main categories and popular items, a short FAQ.\n* `Organization`, `WebSite`, `ItemList`s of categories and popular items, `FAQPage`.\n\n**Internal linking**\n* Render related links from the route registry with a shared `RelatedLinks` component and the framework's link component (`next/link`, `<Link>`), never hand-typed `<a>` tags that go stale.\n* Link each page to its category, 3 to 4 siblings, and the category to every child."
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
"id": "site-wide",
|
|
67
|
-
"title": "Site-Wide Essentials",
|
|
68
|
-
"content": "Set these once in the root layout and every page inherits them.\n\n* `<html lang=\"...\">`, `<meta name=\"viewport\">`, `<meta charset=\"utf-8\">`.\n* A default `og:image` (1200x630), overridden per page or per category where you can.\n* `Organization` with `logo` and `sameAs` (GitHub, LinkedIn, X, YouTube, ...) and `WebSite` with `publisher`, both with stable `@id`s.\n* Search engine verification: `google-site-verification` and `msvalidate.01` meta tags (or DNS verification).\n* `manifest.webmanifest`, `theme-color`, favicon (SVG + ICO) and `apple-touch-icon` (180x180).\n* Privacy-friendly analytics that separates organic and AI-referral traffic.\n* Fonts with `font-display: swap`; the theme set before first paint.\n* Trust pages linked in the footer: About, Contact (with a real email address), Privacy Policy, Terms, and a Disclaimer where relevant.\n* `robots.txt` and the sitemap generated by the build, not maintained by hand."
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"id": "ads-readiness",
|
|
72
|
-
"title": "Ads and Monetisation Without Hurting SEO",
|
|
73
|
-
"content": "Ad networks (Google AdSense and others) review trust and content quality, and badly loaded ads hurt Core Web Vitals.\n\n* Publish `/ads.txt` at the site root, generated at build time from your publisher ID.\n* Add the network's account verification meta tag in the root layout.\n* Load ad scripts after the page is interactive (`strategy=\"afterInteractive\"` or `lazyOnload` with `next/script`), and only when the publisher ID is configured.\n* Reserve each slot's size before it loads, and fill slots only when they come near the viewport, to avoid layout shift.\n* Never push the main content or the tool below the fold with ads, and never place ads inside the H1/lead block.\n* Have About, Contact, Privacy (with the ads and cookie disclosure), Terms and Disclaimer pages live before applying.\n* Keep substantial, original content on every monetised page. Thin pages with ads are the most common rejection reason."
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
"id": "off-page-monitoring",
|
|
77
|
-
"title": "Off-Page Authority and Monitoring",
|
|
78
|
-
"content": "**Authority**\n* Earn links, don't buy them: publish genuinely useful resources (templates, benchmarks, free tools, open-source code), get listed in relevant directories and \"awesome\" lists, write guest posts and answer questions where your audience asks them.\n* Digital PR: original data and strong opinions get cited by journalists and by AI engines.\n* Keep the brand consistent across GitHub, npm, social profiles and directories.\n\n**Setup on launch**\n* Google Search Console: verify the domain property, submit the sitemap, check indexing and Core Web Vitals reports.\n* Bing Webmaster Tools: verify (you can import from Search Console), submit the sitemap, enable IndexNow.\n* Analytics that separate organic search and AI referrals (chatgpt.com, perplexity.ai, gemini.google.com, copilot.microsoft.com).\n\n**Ongoing**\n* Weekly: Search Console coverage errors, new queries on page 2 worth improving.\n* Monthly: rankings for target keywords, pages losing clicks, Core Web Vitals, broken links, which sources AI engines cite for your target questions.\n* Refresh top pages every few months: new facts, new FAQs, updated `dateModified`."
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
"id": "launch-checklist",
|
|
82
|
-
"title": "SEO / AEO / GEO Launch Checklist",
|
|
83
|
-
"content": "Run this before any page or site goes live. Every item is pass or fail.\n\n**Check the built HTML, not the source code.** Run the production build and inspect the output (`view-source:`, or grep the static `out/` / `dist/` HTML). Bugs like client-only pages, duplicate schema and missing images only show up there.\n\n**Crawl and index**\n- [ ] Content, meta and JSON-LD are in the server-rendered HTML (view source, not DevTools)\n- [ ] One absolute canonical per page; all host/protocol/slash variants 301 to it\n- [ ] robots.txt allows the site, references the sitemap, has an explicit AI bot policy\n- [ ] sitemap.xml lists only canonical 200 pages with accurate lastmod, submitted to Google and Bing\n- [ ] Utility and thin pages are noindex; no important page is noindex by mistake\n- [ ] No orphan pages; every key page within 3 clicks of home\n- [ ] Real 404 status for missing pages\n- [ ] Every page has exactly one H1 in the built HTML (a missing H1 usually means the page renders client-only)\n- [ ] Sitemap and llms.txt list only final URLs (no 301s), generated from one route registry\n- [ ] Sitemap lastmod reflects real content changes\n\n**On-page**\n- [ ] Unique title, 50 to 60 characters, keyword first, brand last\n- [ ] Unique meta description, 140 to 155 characters\n- [ ] Exactly one H1; H2/H3 in order; several H2s phrased as questions\n- [ ] Primary keyword in the first 100 words\n- [ ] Descriptive alt text on meaningful images\n- [ ] OpenGraph + Twitter tags with a 1200x630 image on every page, including category and static pages\n- [ ] Brand suffix identical on every title, never missing and never doubled\n\n**Structured data**\n- [ ] One woven `@graph`: Organization, WebSite, WebPage, BreadcrumbList + page type node\n- [ ] FAQPage only where the FAQ is visible, text identical\n- [ ] At most one node of each type per page (no duplicate SoftwareApplication, FAQPage, BreadcrumbList)\n- [ ] Visible steps/FAQs and their schema come from the same data, and nothing is rendered twice\n- [ ] No invented reviews, ratings or prices\n- [ ] Passes Rich Results Test and validator.schema.org\n\n**AEO / GEO**\n- [ ] 40 to 60 word direct answer under each question heading\n- [ ] 4 to 8 page-specific FAQs\n- [ ] /llms.txt published and generated from the route list\n- [ ] Organization `sameAs` links and consistent brand naming\n- [ ] Visible \"Last updated\" date on content that ages\n\n**Performance**\n- [ ] LCP < 2.5s, INP < 200ms, CLS < 0.1 on mobile\n- [ ] LCP image high priority, below-fold media lazy, all media sized\n\nUse `neuctra-ui-seo-setup` with action `audit` to check a page against the on-page and schema items automatically."
|
|
84
|
-
}
|
|
85
|
-
],
|
|
86
|
-
"implementationSteps": {
|
|
87
|
-
"purpose": "Apply the SEO rules to the project in order. First identify framework, router, rendering mode, and current metadata conventions. Keep content and structured data consistent with what users can see.",
|
|
88
|
-
"steps": [
|
|
89
|
-
{
|
|
90
|
-
"number": 1,
|
|
91
|
-
"title": "Identify framework and rendering",
|
|
92
|
-
"details": "Inspect package.json, framework config, router, and build output. Determine whether each route is server rendered, statically generated, prerendered, or client only. Keep page title, description, canonical, visible content, and JSON-LD in the initial HTML whenever possible."
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"number": 2,
|
|
96
|
-
"title": "Map search intent and keywords",
|
|
97
|
-
"details": "Assign one primary search intent and one primary keyword to each canonical URL. Record close variants, audience, and task. Check existing routes to prevent cannibalization; use variants naturally in the title, H1, introduction, subheadings, body, links, and real FAQ questions. Do not add a meta keywords tag for ranking."
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
"number": 3,
|
|
101
|
-
"title": "Write page content and headings",
|
|
102
|
-
"details": "Write a unique title and useful page content. Use exactly one descriptive H1 that matches intent and is close to the title. Keep H2/H3 hierarchy in order. Put the primary keyword or close variant in the opening 100 words. Answer question headings directly and completely; use semantic HTML, descriptive links, and useful image alt text."
|
|
103
|
-
},
|
|
104
|
-
{
|
|
105
|
-
"number": 4,
|
|
106
|
-
"title": "Set metadata and canonical URL",
|
|
107
|
-
"details": "Set a unique title (usually 50–60 characters) with primary topic first and the brand suffix exactly once. Set a unique 140–155 character description that accurately describes the page and reason to click. Add one absolute canonical, robots directives, language, and OpenGraph/Twitter metadata with a real 1200×630 social image. Noindex private or low-value utility pages; do not block a page in robots.txt if crawlers must read its noindex."
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
"number": 5,
|
|
111
|
-
"title": "Add JSON-LD for entities and GEO clarity",
|
|
112
|
-
"details": "Emit valid JSON-LD in the initial HTML. Use one connected @graph per page with Organization, WebSite, WebPage (or suitable subtype), BreadcrumbList, and the relevant main entity. Link nodes with stable @id values. Add FAQPage only for questions and answers visible on the page; use Product, Offer, SoftwareApplication, BlogPosting, TechArticle, HowTo, or ItemList only when the corresponding facts and content are visible. Never invent prices, ratings, reviews, authors, or features. Structured data helps machines understand entities but does not guarantee rankings or citations."
|
|
113
|
-
},
|
|
114
|
-
{
|
|
115
|
-
"number": 6,
|
|
116
|
-
"title": "Implement AEO with useful FAQs and direct answers",
|
|
117
|
-
"details": "For important questions, use a clear question heading followed immediately by a concise, self-contained answer, then supporting detail. Add around 4–8 genuine page-specific FAQs when they help users. Keep FAQ text identical between visible content and JSON-LD. Prefer semantic lists for steps and real tables for comparisons."
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
"number": 7,
|
|
121
|
-
"title": "Complete technical SEO",
|
|
122
|
-
"details": "Use stable lowercase URLs, working internal links, correct 200/301/404/410 status codes, HTTPS, a robots.txt with the sitemap location, and an XML sitemap containing only canonical indexable URLs with meaningful lastmod dates. Add redirects for changed URLs. Ensure important pages are reachable and not orphaned. Provide hreflang only for actual localized variants."
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
"number": 8,
|
|
126
|
-
"title": "Optimize performance and accessibility",
|
|
127
|
-
"details": "Measure field Core Web Vitals and target LCP under 2.5s, INP under 200ms, and CLS under 0.1 at the 75th percentile. Prioritize the actual LCP asset, lazy-load below-fold media, size images, reserve layout space, reduce main-thread work, and use semantic accessible HTML."
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
"number": 9,
|
|
131
|
-
"title": "Build trust and freshness",
|
|
132
|
-
"details": "Show real authorship, evidence, contact/about/policy information, and accurate published/updated dates where relevant. Update dateModified only after a material content change. Keep brand name, logo, and description consistent across the site and trusted profiles."
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
"number": 10,
|
|
136
|
-
"title": "Verify production output",
|
|
137
|
-
"details": "Build the site and inspect the generated or server-rendered HTML, not only source components or the browser DOM after JavaScript runs. Confirm title, description, canonical, robots, H1, links, social tags, JSON-LD validity, and sitemap URLs. Check status codes, mobile performance, Search Console, and Bing Webmaster Tools; fix issues and repeat."
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
"frameworkSpecific": {
|
|
141
|
-
"nextjs-app": {
|
|
142
|
-
"identify": "Next.js App Router routes live under app/ or src/app/.",
|
|
143
|
-
"metadata": "Export metadata or generateMetadata from each server page/layout. Set metadataBase in the root layout. Use title.template once for the brand suffix; use title.absolute for a prebuilt full title. A page marked use client cannot export metadata, so place metadata in its server parent/layout.",
|
|
144
|
-
"jsonLd": "Render a script type application/ld+json from a Server Component. Serialize safely, escaping < as \\u003c. Keep JSON-LD aligned with visible page content.",
|
|
145
|
-
"technical": "Use app/sitemap.js and app/robots.js or equivalent route files. Set canonical with alternates.canonical; configure OpenGraph, Twitter, robots and images. With output: export, confirm every dynamic route is statically generated and inspect out/ HTML."
|
|
146
|
-
},
|
|
147
|
-
"nextjs-pages": {
|
|
148
|
-
"identify": "Next.js Pages Router routes live under pages/ or src/pages/.",
|
|
149
|
-
"metadata": "Use next/head in each page for title, description, canonical, robots and social meta tags. Ensure metadata is present during SSR/SSG, not only after client effects.",
|
|
150
|
-
"jsonLd": "Render a JSON-LD script in the page with dangerouslySetInnerHTML and safely escaped serialized data.",
|
|
151
|
-
"technical": "Use getStaticProps/getStaticPaths or SSR as appropriate. Generate sitemap and robots files from canonical route data, and inspect the built HTML."
|
|
152
|
-
},
|
|
153
|
-
"react-vite": {
|
|
154
|
-
"identify": "Vite with React is commonly a client-rendered SPA; inspect whether routes are prerendered or SSR.",
|
|
155
|
-
"metadata": "For React 19, native title/meta/link hoisting may be used; for React 18 use a head manager such as react-helmet-async. Client-only updates are not sufficient for crawler visibility: prerender routes or add SSR for indexable pages.",
|
|
156
|
-
"jsonLd": "Place the JSON-LD script in the rendered document head/body and verify it is present in the prerendered HTML. Avoid adding schema only in an effect.",
|
|
157
|
-
"technical": "Generate a static HTML page per indexable route or use an SSR/prerender solution. Configure host fallback and real 404 handling, canonical URLs, robots.txt, and sitemap.xml; inspect dist output."
|
|
158
|
-
},
|
|
159
|
-
"react-router": {
|
|
160
|
-
"identify": "Determine React Router version and whether framework/SSR mode, prerendering, or SPA mode is enabled.",
|
|
161
|
-
"metadata": "In React Router framework mode, export a route meta function returning title, description, canonical link and social metadata. Ensure route metadata is rendered server-side or during prerender.",
|
|
162
|
-
"jsonLd": "Render JSON-LD in the route component or root document using route data; escape serialized JSON and ensure it appears in built HTML.",
|
|
163
|
-
"technical": "Use framework-mode SSR/prerender where pages need indexing. Generate sitemap and robots from route data; inspect actual production responses and missing-route status codes."
|
|
164
|
-
},
|
|
165
|
-
"remix": {
|
|
166
|
-
"identify": "Inspect whether the project uses Remix conventions or React Router framework mode; follow its installed version and route structure.",
|
|
167
|
-
"metadata": "Export meta from each route module. Return title, description, canonical and social meta descriptors. Compose parent and route metadata deliberately so child routes do not lose required tags.",
|
|
168
|
-
"jsonLd": "Render a script tag in the route document using loader-backed page data and safely serialized JSON-LD.",
|
|
169
|
-
"technical": "Use server rendering for indexable route content. Build sitemap.xml and robots.txt from route definitions, return correct HTTP status codes, and verify the response HTML."
|
|
170
|
-
},
|
|
171
|
-
"other": {
|
|
172
|
-
"identify": "Confirm the framework, router, rendering strategy, and metadata API from the project files and installed version.",
|
|
173
|
-
"metadata": "Use the framework’s server-rendered or build-time head/metadata API. Do not rely solely on client-side effects for indexable pages.",
|
|
174
|
-
"jsonLd": "Add a valid JSON-LD script to initial HTML using safe serialization.",
|
|
175
|
-
"technical": "Generate canonical URLs, robots rules, sitemap and redirects using the framework or hosting platform, then verify production responses."
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
"assistantGuidance": "When framework is supplied, use only its matching implementation recipe plus shared rules. When framework is auto or unknown, inspect project files and infer the framework and render mode; ask the user only if repository evidence is ambiguous. Return implementation guidance tailored to the project rather than blindly applying snippets from another framework."
|
|
179
|
-
}
|
|
180
|
-
}
|