react-ssr-seo-toolkit 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/README.md +117 -324
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  <br />
4
4
 
5
- <img src="https://img.shields.io/badge/react--ssr--seo-v1.0.1-000000?style=for-the-badge&labelColor=000000" alt="react-ssr-seo" />
5
+ <img src="https://img.shields.io/badge/react--ssr--seo--toolkit-v1.0.3-000000?style=for-the-badge&labelColor=000000" alt="react-ssr-seo-toolkit" />
6
6
 
7
7
  <br />
8
8
  <br />
9
9
 
10
- # `react-ssr-seo`
10
+ # `react-ssr-seo-toolkit`
11
11
 
12
12
  ### The Complete SEO Toolkit for React SSR Applications
13
13
 
@@ -19,7 +19,7 @@
19
19
  &nbsp;
20
20
  [![License](https://img.shields.io/npm/l/react-ssr-seo-toolkit?style=for-the-badge&color=blue)](./LICENSE)
21
21
  &nbsp;
22
- [![Bundle](https://img.shields.io/bundlephobia/minzip/react-ssr-seo?style=for-the-badge&label=size&color=success)](https://bundlephobia.com/package/react-ssr-seo-toolkit)
22
+ [![Bundle](https://img.shields.io/bundlephobia/minzip/react-ssr-seo-toolkit?style=for-the-badge&label=size&color=success)](https://bundlephobia.com/package/react-ssr-seo-toolkit)
23
23
 
24
24
  <br />
25
25
 
@@ -98,46 +98,72 @@ All in one package. Zero dependencies. Fully typed. SSR-safe.
98
98
  npm install react-ssr-seo-toolkit
99
99
  ```
100
100
 
101
- ```bash
102
- # or
103
- pnpm add react-ssr-seo-toolkit # yarn add react-ssr-seo-toolkit # bun add react-ssr-seo-toolkit
104
- ```
101
+ > **Requires:** `react >= 18.0.0` as a peer dependency. Zero other dependencies.
102
+
103
+ <br />
104
+
105
+ ### 2. Project Structure
105
106
 
106
- > **Requires:** `react >= 18.0.0` as peer dependency
107
+ You only need **two new files** a shared SEO config and your page components:
108
+
109
+ ```
110
+ my-app/
111
+ ├── config/
112
+ │ └── seo.ts ← shared SEO defaults (Step 3)
113
+ ├── pages/
114
+ │ ├── HomePage.tsx ← each page merges its own SEO
115
+ │ ├── AboutPage.tsx
116
+ │ └── BlogPost.tsx
117
+ ├── components/
118
+ │ └── Layout.tsx ← wraps <SEOHead> + <JsonLd>
119
+ ├── server.tsx ← Express / SSR entry point
120
+ └── package.json
121
+ ```
107
122
 
108
123
  <br />
109
124
 
110
- ### 2. Create Site Config (once)
125
+ ### 3. Create Site Config (once)
126
+
127
+ This file holds defaults that every page inherits. Pages override only what they need.
111
128
 
112
129
  ```tsx
130
+ // config/seo.ts
113
131
  import { createSEOConfig } from "react-ssr-seo-toolkit";
114
132
 
115
- const siteConfig = createSEOConfig({
133
+ export const siteConfig = createSEOConfig({
116
134
  titleTemplate: "%s | MySite", // auto-appends " | MySite" to every page title
117
- openGraph: { siteName: "MySite", type: "website" },
135
+ description: "Default site description for SEO.",
136
+ openGraph: { siteName: "MySite", type: "website", locale: "en_US" },
118
137
  twitter: { card: "summary_large_image", site: "@mysite" },
119
138
  });
139
+
140
+ export const SITE_URL = "https://mysite.com";
120
141
  ```
121
142
 
143
+ > **Tip:** `titleTemplate` uses `%s` as a placeholder. Setting `title: "About"` renders as `About | MySite`.
144
+
122
145
  <br />
123
146
 
124
- ### 3. Add to Any Page
147
+ ### 4. Add to Any Page
148
+
149
+ Merge the shared config with page-specific values, then render with `<SEOHead>`.
125
150
 
126
151
  ```tsx
152
+ // pages/AboutPage.tsx
127
153
  import { SEOHead, mergeSEOConfig, buildCanonicalUrl } from "react-ssr-seo-toolkit";
154
+ import { siteConfig, SITE_URL } from "../config/seo";
128
155
 
129
- function AboutPage() {
156
+ export function AboutPage() {
130
157
  const seo = mergeSEOConfig(siteConfig, {
131
158
  title: "About Us",
132
159
  description: "Learn about our company and mission.",
133
- canonical: buildCanonicalUrl("https://mysite.com", "/about"),
160
+ canonical: buildCanonicalUrl(SITE_URL, "/about"),
134
161
  });
135
162
 
136
163
  return (
137
164
  <html>
138
165
  <head>
139
166
  <SEOHead {...seo} />
140
- {/* Renders: <title>, <meta>, <link>, <script type="application/ld+json"> */}
141
167
  </head>
142
168
  <body>
143
169
  <h1>About Us</h1>
@@ -147,7 +173,7 @@ function AboutPage() {
147
173
  }
148
174
  ```
149
175
 
150
- **Done.** That's all you need for basic SEO. Keep reading for real-world examples.
176
+ **That's it.** You now have full SEO on every page. Keep reading for structured data and framework examples.
151
177
 
152
178
  <br />
153
179
 
@@ -163,29 +189,21 @@ function AboutPage() {
163
189
 
164
190
  ### Blog / Article Page
165
191
 
166
- <details open>
167
- <summary><strong>Click to expand full example</strong></summary>
168
-
169
192
  ```tsx
193
+ // pages/BlogPost.tsx
170
194
  import {
171
195
  SEOHead, JsonLd,
172
- createSEOConfig, mergeSEOConfig, buildCanonicalUrl,
196
+ mergeSEOConfig, buildCanonicalUrl,
173
197
  createArticleSchema, createBreadcrumbSchema,
174
198
  } from "react-ssr-seo-toolkit";
199
+ import { siteConfig, SITE_URL } from "../config/seo";
175
200
 
176
- // Site config (create once, reuse everywhere)
177
- const siteConfig = createSEOConfig({
178
- titleTemplate: "%s | My Blog",
179
- openGraph: { siteName: "My Blog", type: "website" },
180
- twitter: { card: "summary_large_image", site: "@myblog" },
181
- });
182
-
183
- function BlogPostPage() {
201
+ export function BlogPostPage() {
184
202
  // ── Page SEO ──────────────────────────────────────────────
185
203
  const seo = mergeSEOConfig(siteConfig, {
186
204
  title: "How to Build an SSR App",
187
205
  description: "A complete guide to building server-rendered React apps with proper SEO.",
188
- canonical: buildCanonicalUrl("https://myblog.com", "/blog/ssr-guide"),
206
+ canonical: buildCanonicalUrl(SITE_URL, "/blog/ssr-guide"),
189
207
  openGraph: {
190
208
  title: "How to Build an SSR App",
191
209
  description: "A complete guide to SSR with React.",
@@ -246,10 +264,9 @@ function BlogPostPage() {
246
264
  }
247
265
  ```
248
266
 
249
- </details>
267
+ <br />
250
268
 
251
- <details>
252
- <summary><strong>See the HTML output this generates</strong></summary>
269
+ ### Generated HTML Output
253
270
 
254
271
  ```html
255
272
  <head>
@@ -257,40 +274,26 @@ function BlogPostPage() {
257
274
  <title>How to Build an SSR App | My Blog</title>
258
275
  <meta name="description" content="A complete guide to building server-rendered React apps..." />
259
276
  <link rel="canonical" href="https://myblog.com/blog/ssr-guide" />
260
- <meta name="robots" content="index, follow" />
261
277
 
262
- <!-- Open Graph (Facebook, LinkedIn, etc.) -->
278
+ <!-- Open Graph -->
263
279
  <meta property="og:title" content="How to Build an SSR App" />
264
280
  <meta property="og:description" content="A complete guide to SSR with React." />
265
281
  <meta property="og:type" content="article" />
266
282
  <meta property="og:url" content="https://myblog.com/blog/ssr-guide" />
267
283
  <meta property="og:site_name" content="My Blog" />
268
284
  <meta property="og:image" content="https://myblog.com/images/ssr-guide.jpg" />
269
- <meta property="og:image:width" content="1200" />
270
- <meta property="og:image:height" content="630" />
271
- <meta property="og:image:alt" content="SSR Guide Cover" />
272
285
 
273
286
  <!-- Twitter Card -->
274
287
  <meta name="twitter:card" content="summary_large_image" />
275
288
  <meta name="twitter:site" content="@myblog" />
276
- <meta name="twitter:creator" content="@authorhandle" />
277
289
  <meta name="twitter:title" content="How to Build an SSR App" />
278
- <meta name="twitter:image" content="https://myblog.com/images/ssr-guide.jpg" />
279
-
280
- <!-- JSON-LD: Article -->
281
- <script type="application/ld+json">
282
- {"@context":"https://schema.org","@type":"Article","headline":"How to Build an SSR App",...}
283
- </script>
284
290
 
285
- <!-- JSON-LD: Breadcrumbs -->
286
- <script type="application/ld+json">
287
- {"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[...]}
288
- </script>
291
+ <!-- JSON-LD -->
292
+ <script type="application/ld+json">{"@context":"https://schema.org","@type":"Article",...}</script>
293
+ <script type="application/ld+json">{"@context":"https://schema.org","@type":"BreadcrumbList",...}</script>
289
294
  </head>
290
295
  ```
291
296
 
292
- </details>
293
-
294
297
  <br />
295
298
 
296
299
  ---
@@ -299,21 +302,13 @@ function BlogPostPage() {
299
302
 
300
303
  ### E-Commerce Product Page
301
304
 
302
- <details open>
303
- <summary><strong>Click to expand full example</strong></summary>
304
-
305
305
  ```tsx
306
306
  import {
307
307
  SEOHead, JsonLd,
308
- createSEOConfig, mergeSEOConfig, buildCanonicalUrl,
308
+ mergeSEOConfig, buildCanonicalUrl,
309
309
  createProductSchema, createBreadcrumbSchema,
310
310
  } from "react-ssr-seo-toolkit";
311
-
312
- const siteConfig = createSEOConfig({
313
- titleTemplate: "%s | Acme Store",
314
- openGraph: { siteName: "Acme Store", type: "website" },
315
- twitter: { card: "summary_large_image", site: "@acmestore" },
316
- });
311
+ import { siteConfig, SITE_URL } from "../config/seo";
317
312
 
318
313
  function ProductPage() {
319
314
  const product = {
@@ -328,9 +323,8 @@ function ProductPage() {
328
323
  reviewCount: 342,
329
324
  };
330
325
 
331
- const url = buildCanonicalUrl("https://acmestore.com", "/products/ergonomic-keyboard");
326
+ const url = buildCanonicalUrl(SITE_URL, "/products/ergonomic-keyboard");
332
327
 
333
- // ── Page SEO ──────────────────────────────────────────────
334
328
  const seo = mergeSEOConfig(siteConfig, {
335
329
  title: product.name,
336
330
  description: product.description,
@@ -344,7 +338,6 @@ function ProductPage() {
344
338
  },
345
339
  });
346
340
 
347
- // ── Structured Data ───────────────────────────────────────
348
341
  const schema = createProductSchema({
349
342
  name: product.name,
350
343
  url,
@@ -359,31 +352,21 @@ function ProductPage() {
359
352
  reviewCount: product.reviewCount,
360
353
  });
361
354
 
362
- const breadcrumbs = createBreadcrumbSchema([
363
- { name: "Home", url: "https://acmestore.com" },
364
- { name: "Products", url: "https://acmestore.com/products" },
365
- { name: product.name, url },
366
- ]);
367
-
368
355
  return (
369
356
  <html>
370
357
  <head>
371
358
  <SEOHead {...seo} />
372
359
  <JsonLd data={schema} />
373
- <JsonLd data={breadcrumbs} />
374
360
  </head>
375
361
  <body>
376
362
  <h1>{product.name}</h1>
377
- <p>{product.description}</p>
378
- <p>${product.price} — {product.inStock ? "In Stock" : "Out of Stock"}</p>
363
+ <p>${product.price}</p>
379
364
  </body>
380
365
  </html>
381
366
  );
382
367
  }
383
368
  ```
384
369
 
385
- </details>
386
-
387
370
  <br />
388
371
 
389
372
  ---
@@ -392,14 +375,12 @@ function ProductPage() {
392
375
 
393
376
  ### FAQ Page
394
377
 
395
- <details open>
396
- <summary><strong>Click to expand full example</strong></summary>
397
-
398
378
  ```tsx
399
379
  import {
400
380
  SEOHead, JsonLd,
401
381
  mergeSEOConfig, buildCanonicalUrl, createFAQSchema,
402
382
  } from "react-ssr-seo-toolkit";
383
+ import { siteConfig, SITE_URL } from "../config/seo";
403
384
 
404
385
  function FAQPage() {
405
386
  const faqs = [
@@ -411,7 +392,7 @@ function FAQPage() {
411
392
  const seo = mergeSEOConfig(siteConfig, {
412
393
  title: "FAQ",
413
394
  description: "Frequently asked questions about our products and services.",
414
- canonical: buildCanonicalUrl("https://mysite.com", "/faq"),
395
+ canonical: buildCanonicalUrl(SITE_URL, "/faq"),
415
396
  });
416
397
 
417
398
  return (
@@ -434,8 +415,6 @@ function FAQPage() {
434
415
  }
435
416
  ```
436
417
 
437
- </details>
438
-
439
418
  <br />
440
419
 
441
420
  ---
@@ -444,15 +423,13 @@ function FAQPage() {
444
423
 
445
424
  ### Homepage (Organization + Website Schema)
446
425
 
447
- <details>
448
- <summary><strong>Click to expand full example</strong></summary>
449
-
450
426
  ```tsx
451
427
  import {
452
428
  SEOHead, JsonLd,
453
429
  mergeSEOConfig,
454
430
  createOrganizationSchema, createWebsiteSchema,
455
431
  } from "react-ssr-seo-toolkit";
432
+ import { siteConfig } from "../config/seo";
456
433
 
457
434
  function HomePage() {
458
435
  const seo = mergeSEOConfig(siteConfig, {
@@ -506,8 +483,6 @@ function HomePage() {
506
483
  }
507
484
  ```
508
485
 
509
- </details>
510
-
511
486
  <br />
512
487
 
513
488
  ---
@@ -524,7 +499,6 @@ const seo = mergeSEOConfig(siteConfig, {
524
499
  { hreflang: "en", href: "https://mysite.com/en/products" },
525
500
  { hreflang: "es", href: "https://mysite.com/es/products" },
526
501
  { hreflang: "fr", href: "https://mysite.com/fr/products" },
527
- { hreflang: "de", href: "https://mysite.com/de/products" },
528
502
  { hreflang: "x-default", href: "https://mysite.com/products" },
529
503
  ],
530
504
  });
@@ -532,7 +506,6 @@ const seo = mergeSEOConfig(siteConfig, {
532
506
  // Generates:
533
507
  // <link rel="alternate" hreflang="en" href="https://mysite.com/en/products" />
534
508
  // <link rel="alternate" hreflang="es" href="https://mysite.com/es/products" />
535
- // <link rel="alternate" hreflang="fr" href="https://mysite.com/fr/products" />
536
509
  // ...
537
510
  ```
538
511
 
@@ -605,9 +578,6 @@ const combined = composeSchemas(
605
578
 
606
579
  ### Next.js App Router
607
580
 
608
- <details open>
609
- <summary><strong>Using with <code>generateMetadata()</code></strong></summary>
610
-
611
581
  ```tsx
612
582
  // app/blog/[slug]/page.tsx
613
583
  import {
@@ -657,15 +627,10 @@ export default function BlogPost({ params }) {
657
627
  }
658
628
  ```
659
629
 
660
- </details>
661
-
662
630
  <br />
663
631
 
664
632
  ### Next.js Pages Router
665
633
 
666
- <details>
667
- <summary><strong>Using with <code>next/head</code></strong></summary>
668
-
669
634
  ```tsx
670
635
  // pages/about.tsx
671
636
  import Head from "next/head";
@@ -691,34 +656,19 @@ export default function AboutPage() {
691
656
  }
692
657
  ```
693
658
 
694
- </details>
695
-
696
659
  <br />
697
660
 
698
661
  ### React Router 7 SSR
699
662
 
700
- <details>
701
- <summary><strong>Using in root document</strong></summary>
702
-
703
663
  ```tsx
704
664
  // app/root.tsx
705
- import { SEOHead, JsonLd, createSEOConfig, mergeSEOConfig, createOrganizationSchema } from "react-ssr-seo-toolkit";
706
-
707
- const siteConfig = createSEOConfig({
708
- titleTemplate: "%s — Acme",
709
- openGraph: { siteName: "Acme", type: "website", locale: "en_US" },
710
- twitter: { card: "summary_large_image", site: "@acme" },
711
- });
665
+ import { SEOHead, mergeSEOConfig, createOrganizationSchema, JsonLd } from "react-ssr-seo-toolkit";
666
+ import { siteConfig } from "./config/seo";
712
667
 
713
668
  export function HomePage() {
714
669
  const seo = mergeSEOConfig(siteConfig, {
715
670
  title: "Home",
716
671
  canonical: "https://acme.com",
717
- jsonLd: createOrganizationSchema({
718
- name: "Acme",
719
- url: "https://acme.com",
720
- logo: "https://acme.com/logo.png",
721
- }),
722
672
  });
723
673
 
724
674
  return (
@@ -736,27 +686,19 @@ export function HomePage() {
736
686
  }
737
687
  ```
738
688
 
739
- </details>
740
-
741
689
  <br />
742
690
 
743
691
  ### Express + React SSR
744
692
 
745
- <details>
746
- <summary><strong>Using with <code>renderToString()</code></strong></summary>
747
-
748
693
  ```tsx
694
+ // server.tsx
749
695
  import express from "express";
750
696
  import { renderToString } from "react-dom/server";
751
- import { SEOHead, JsonLd, createSEOConfig, mergeSEOConfig, createProductSchema } from "react-ssr-seo-toolkit";
697
+ import { SEOHead, JsonLd, mergeSEOConfig, createProductSchema } from "react-ssr-seo-toolkit";
698
+ import { siteConfig } from "./config/seo";
752
699
 
753
700
  const app = express();
754
701
 
755
- const siteConfig = createSEOConfig({
756
- titleTemplate: "%s | My Store",
757
- openGraph: { siteName: "My Store" },
758
- });
759
-
760
702
  function ProductPage({ product }) {
761
703
  const seo = mergeSEOConfig(siteConfig, {
762
704
  title: product.name,
@@ -791,8 +733,6 @@ app.get("/products/:id", (req, res) => {
791
733
  app.listen(3000);
792
734
  ```
793
735
 
794
- </details>
795
-
796
736
  <br />
797
737
 
798
738
  ---
@@ -803,161 +743,55 @@ app.listen(3000);
803
743
 
804
744
  ### Config Builders
805
745
 
806
- <table>
807
- <tr>
808
- <th>Function</th>
809
- <th>What It Does</th>
810
- </tr>
811
- <tr>
812
- <td><code>createSEOConfig(config?)</code></td>
813
- <td>Create a normalized SEO config. Use for site-wide defaults.</td>
814
- </tr>
815
- <tr>
816
- <td><code>mergeSEOConfig(base, override)</code></td>
817
- <td>Deep-merge site config with page-level overrides. Arrays are replaced, not concatenated.</td>
818
- </tr>
819
- <tr>
820
- <td><code>normalizeSEOConfig(config)</code></td>
821
- <td>Trim strings, normalize URLs, clean up a config object.</td>
822
- </tr>
823
- </table>
746
+ | Function | What It Does |
747
+ |---|---|
748
+ | `createSEOConfig(config?)` | Create a normalized SEO config. Use for site-wide defaults. |
749
+ | `mergeSEOConfig(base, override)` | Deep-merge site config with page-level overrides. Arrays are replaced, not concatenated. |
750
+ | `normalizeSEOConfig(config)` | Trim strings, normalize URLs, clean up a config object. |
824
751
 
825
752
  <br />
826
753
 
827
754
  ### Metadata Helpers
828
755
 
829
- <table>
830
- <tr>
831
- <th>Function</th>
832
- <th>Example</th>
833
- <th>Result</th>
834
- </tr>
835
- <tr>
836
- <td><code>buildTitle(title, template)</code></td>
837
- <td><code>buildTitle("About", "%s | MySite")</code></td>
838
- <td><code>"About | MySite"</code></td>
839
- </tr>
840
- <tr>
841
- <td><code>buildDescription(desc, maxLen)</code></td>
842
- <td><code>buildDescription("Long text...", 160)</code></td>
843
- <td>Truncated at 160 chars with ellipsis</td>
844
- </tr>
845
- <tr>
846
- <td><code>buildCanonicalUrl(base, path)</code></td>
847
- <td><code>buildCanonicalUrl("https://x.com", "/about")</code></td>
848
- <td><code>"https://x.com/about"</code></td>
849
- </tr>
850
- <tr>
851
- <td><code>buildRobotsDirectives(config)</code></td>
852
- <td><code>buildRobotsDirectives({ index: false, follow: true })</code></td>
853
- <td><code>"noindex, follow"</code></td>
854
- </tr>
855
- <tr>
856
- <td><code>noIndex()</code></td>
857
- <td><code>noIndex()</code></td>
858
- <td><code>{ index: false, follow: true }</code></td>
859
- </tr>
860
- <tr>
861
- <td><code>noIndexNoFollow()</code></td>
862
- <td><code>noIndexNoFollow()</code></td>
863
- <td><code>{ index: false, follow: false }</code></td>
864
- </tr>
865
- <tr>
866
- <td><code>buildOpenGraph(config)</code></td>
867
- <td><code>buildOpenGraph({ title: "Hi" })</code></td>
868
- <td><code>[{ property: "og:title", content: "Hi" }]</code></td>
869
- </tr>
870
- <tr>
871
- <td><code>buildTwitterMetadata(config)</code></td>
872
- <td><code>buildTwitterMetadata({ card: "summary" })</code></td>
873
- <td><code>[{ name: "twitter:card", content: "summary" }]</code></td>
874
- </tr>
875
- <tr>
876
- <td><code>buildAlternateLinks(alternates)</code></td>
877
- <td><code>buildAlternateLinks([{ hreflang: "en", href: "..." }])</code></td>
878
- <td><code>[{ rel: "alternate", hreflang: "en", href: "..." }]</code></td>
879
- </tr>
880
- </table>
756
+ | Function | Example | Result |
757
+ |---|---|---|
758
+ | `buildTitle(title, template)` | `buildTitle("About", "%s \| MySite")` | `"About \| MySite"` |
759
+ | `buildDescription(desc, maxLen)` | `buildDescription("Long text...", 160)` | Truncated at 160 chars |
760
+ | `buildCanonicalUrl(base, path)` | `buildCanonicalUrl("https://x.com", "/about")` | `"https://x.com/about"` |
761
+ | `buildRobotsDirectives(config)` | `buildRobotsDirectives({ index: false })` | `"noindex, follow"` |
762
+ | `noIndex()` | `noIndex()` | `{ index: false, follow: true }` |
763
+ | `noIndexNoFollow()` | `noIndexNoFollow()` | `{ index: false, follow: false }` |
764
+ | `buildOpenGraph(config)` | `buildOpenGraph({ title: "Hi" })` | `[{ property: "og:title", content: "Hi" }]` |
765
+ | `buildTwitterMetadata(config)` | `buildTwitterMetadata({ card: "summary" })` | `[{ name: "twitter:card", content: "summary" }]` |
766
+ | `buildAlternateLinks(alternates)` | `buildAlternateLinks([{ hreflang: "en", href: "..." }])` | `[{ rel: "alternate", hreflang: "en", href: "..." }]` |
881
767
 
882
768
  <br />
883
769
 
884
770
  ### JSON-LD Schema Generators
885
771
 
886
- > All return a plain object with `@context: "https://schema.org"` and `@type` set.
772
+ All return a plain object with `@context: "https://schema.org"` and `@type` set.
887
773
 
888
- <table>
889
- <tr>
890
- <th>Function</th>
891
- <th>Schema Type</th>
892
- <th>Use Case</th>
893
- </tr>
894
- <tr>
895
- <td><code>createOrganizationSchema(input)</code></td>
896
- <td>Organization</td>
897
- <td>Company info, logo, social links, contact</td>
898
- </tr>
899
- <tr>
900
- <td><code>createWebsiteSchema(input)</code></td>
901
- <td>WebSite</td>
902
- <td>Site name, sitelinks searchbox</td>
903
- </tr>
904
- <tr>
905
- <td><code>createArticleSchema(input)</code></td>
906
- <td>Article</td>
907
- <td>Blog posts, news articles, authors, dates</td>
908
- </tr>
909
- <tr>
910
- <td><code>createProductSchema(input)</code></td>
911
- <td>Product</td>
912
- <td>E-commerce: price, brand, SKU, ratings, availability</td>
913
- </tr>
914
- <tr>
915
- <td><code>createBreadcrumbSchema(items)</code></td>
916
- <td>BreadcrumbList</td>
917
- <td>Navigation hierarchy</td>
918
- </tr>
919
- <tr>
920
- <td><code>createFAQSchema(items)</code></td>
921
- <td>FAQPage</td>
922
- <td>FAQ pages with question + answer pairs</td>
923
- </tr>
924
- <tr>
925
- <td><code>composeSchemas(...schemas)</code></td>
926
- <td>@graph</td>
927
- <td>Combine multiple schemas into one JSON-LD block</td>
928
- </tr>
929
- </table>
774
+ | Function | Schema Type | Use Case |
775
+ |---|---|---|
776
+ | `createOrganizationSchema(input)` | Organization | Company info, logo, social links, contact |
777
+ | `createWebsiteSchema(input)` | WebSite | Site name, sitelinks searchbox |
778
+ | `createArticleSchema(input)` | Article | Blog posts, news articles, authors, dates |
779
+ | `createProductSchema(input)` | Product | E-commerce: price, brand, SKU, ratings, availability |
780
+ | `createBreadcrumbSchema(items)` | BreadcrumbList | Navigation hierarchy |
781
+ | `createFAQSchema(items)` | FAQPage | FAQ pages with question + answer pairs |
782
+ | `composeSchemas(...schemas)` | @graph | Combine multiple schemas into one JSON-LD block |
930
783
 
931
784
  <br />
932
785
 
933
786
  ### Utilities
934
787
 
935
- <table>
936
- <tr>
937
- <th>Function</th>
938
- <th>What It Does</th>
939
- </tr>
940
- <tr>
941
- <td><code>safeJsonLdSerialize(data)</code></td>
942
- <td>Serialize JSON-LD safely for <code>&lt;script&gt;</code> tags — escapes <code>&lt;</code>, <code>&gt;</code>, <code>&amp;</code> to prevent XSS</td>
943
- </tr>
944
- <tr>
945
- <td><code>normalizeUrl(url)</code></td>
946
- <td>Trim whitespace, remove trailing slashes</td>
947
- </tr>
948
- <tr>
949
- <td><code>buildFullUrl(base, path?)</code></td>
950
- <td>Combine base URL with path</td>
951
- </tr>
952
- <tr>
953
- <td><code>omitEmpty(obj)</code></td>
954
- <td>Remove keys with <code>undefined</code>, <code>null</code>, or empty string values</td>
955
- </tr>
956
- <tr>
957
- <td><code>deepMerge(base, override)</code></td>
958
- <td>Deep-merge two objects (arrays replaced, not concatenated)</td>
959
- </tr>
960
- </table>
788
+ | Function | What It Does |
789
+ |---|---|
790
+ | `safeJsonLdSerialize(data)` | Serialize JSON-LD safely — escapes `<`, `>`, `&` to prevent XSS |
791
+ | `normalizeUrl(url)` | Trim whitespace, remove trailing slashes |
792
+ | `buildFullUrl(base, path?)` | Combine base URL with path |
793
+ | `omitEmpty(obj)` | Remove keys with `undefined`, `null`, or empty string values |
794
+ | `deepMerge(base, override)` | Deep-merge two objects (arrays replaced, not concatenated) |
961
795
 
962
796
  <br />
963
797
 
@@ -996,11 +830,9 @@ Renders all SEO tags as React elements. Place inside `<head>`.
996
830
  ]}
997
831
  additionalMetaTags={[
998
832
  { name: "author", content: "Jane Doe" },
999
- { property: "article:published_time", content: "2025-06-15" },
1000
833
  ]}
1001
834
  additionalLinkTags={[
1002
835
  { rel: "icon", href: "/favicon.ico" },
1003
- { rel: "apple-touch-icon", href: "/apple-touch-icon.png", sizes: "180x180" },
1004
836
  ]}
1005
837
  jsonLd={createArticleSchema({ headline: "...", url: "..." })}
1006
838
  />
@@ -1048,49 +880,26 @@ import type {
1048
880
 
1049
881
  ## Live Demo
1050
882
 
1051
- Try it locally — the repo includes a **working Express SSR demo** with 5 pages:
883
+ The repo includes a **working Express SSR demo** with every feature:
1052
884
 
1053
885
  ```bash
1054
- git clone https://github.com/Tonmoy01/react-ssr-seo.git
1055
- cd react-ssr-seo
886
+ git clone https://github.com/Tonmoy01/react-ssr-seo-toolkit.git
887
+ cd react-ssr-seo-toolkit
1056
888
  npm install
1057
889
  npm run demo
1058
890
  ```
1059
891
 
1060
- Then open your browser:
892
+ Then visit [http://localhost:3000](http://localhost:3000):
1061
893
 
1062
- <table>
1063
- <tr>
1064
- <th>URL</th>
1065
- <th>Page</th>
1066
- <th>SEO Features Demonstrated</th>
1067
- </tr>
1068
- <tr>
1069
- <td><code>localhost:3000</code></td>
1070
- <td>Home</td>
1071
- <td>Organization + Website schema, hreflang, OG images</td>
1072
- </tr>
1073
- <tr>
1074
- <td><code>localhost:3000/article</code></td>
1075
- <td>Article</td>
1076
- <td>Article schema, breadcrumbs, multiple authors, Twitter cards</td>
1077
- </tr>
1078
- <tr>
1079
- <td><code>localhost:3000/product</code></td>
1080
- <td>Product</td>
1081
- <td>Product schema, pricing, ratings, availability, breadcrumbs</td>
1082
- </tr>
1083
- <tr>
1084
- <td><code>localhost:3000/faq</code></td>
1085
- <td>FAQ</td>
1086
- <td>FAQPage schema with Q&A pairs</td>
1087
- </tr>
1088
- <tr>
1089
- <td><code>localhost:3000/noindex</code></td>
1090
- <td>No-Index</td>
1091
- <td>Robots noindex directive</td>
1092
- </tr>
1093
- </table>
894
+ | URL | Page | SEO Features |
895
+ |---|---|---|
896
+ | `/` | Home | Organization + Website schema, hreflang, OG images |
897
+ | `/getting-started` | Getting Started | Installation guide with copy-paste examples |
898
+ | `/article` | Article | Article schema, breadcrumbs, multiple authors, Twitter cards |
899
+ | `/product` | Product | Product schema, pricing, ratings, availability |
900
+ | `/faq` | FAQ | FAQPage schema with Q&A pairs |
901
+ | `/noindex` | No-Index | Robots noindex directive |
902
+ | `/api` | API Reference | Complete function and type documentation |
1094
903
 
1095
904
  > **Tip:** Right-click any page and **View Page Source** to see all SEO tags in the raw HTML.
1096
905
 
@@ -1121,41 +930,25 @@ npm run demo # run demo server
1121
930
 
1122
931
  ## Troubleshooting
1123
932
 
1124
- <details>
1125
- <summary><strong>"Cannot find module 'react-ssr-seo'"</strong></summary>
933
+ ### "Cannot find module 'react-ssr-seo-toolkit'"
1126
934
 
1127
- <br />
1128
-
1129
- Ensure the package is installed and your bundler supports the `exports` field in `package.json`. If using an older bundler, try importing from `react-ssr-seo/dist/index.js` directly.
1130
- </details>
935
+ Ensure the package is installed and your bundler supports the `exports` field in `package.json`. If using an older bundler, try importing from `react-ssr-seo-toolkit/dist/index.js` directly.
1131
936
 
1132
- <details>
1133
- <summary><strong>Hydration mismatch warnings</strong></summary>
1134
-
1135
- <br />
937
+ ### Hydration mismatch warnings
1136
938
 
1137
939
  `<SEOHead>` produces deterministic output. If you see hydration warnings, ensure the same config object is used on both server and client. Avoid using `Date.now()` or random values in your SEO config.
1138
- </details>
1139
940
 
1140
- <details>
1141
- <summary><strong>JSON-LD not appearing in page source</strong></summary>
941
+ ### JSON-LD not appearing in page source
1142
942
 
1143
- <br />
1144
-
1145
- Make sure `<JsonLd>` or `<script type="application/ld+json">` is inside `<head>` and rendered during SSR — not in a client-only `useEffect`.
1146
- </details>
943
+ Make sure `<JsonLd>` is inside `<head>` and rendered during SSR — not in a client-only `useEffect`.
1147
944
 
1148
- <details>
1149
- <summary><strong>TypeScript errors</strong></summary>
1150
-
1151
- <br />
945
+ ### TypeScript errors
1152
946
 
1153
947
  All types are exported. Import them directly:
1154
948
 
1155
949
  ```tsx
1156
950
  import type { SEOConfig, OpenGraphConfig } from "react-ssr-seo-toolkit";
1157
951
  ```
1158
- </details>
1159
952
 
1160
953
  <br />
1161
954
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-ssr-seo-toolkit",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Framework-agnostic SEO utilities, metadata builders, structured data helpers, and React components for SSR applications",
5
5
  "keywords": [
6
6
  "seo",
@@ -24,11 +24,11 @@
24
24
  "license": "MIT",
25
25
  "repository": {
26
26
  "type": "git",
27
- "url": "https://github.com/Tonmoy01/react-ssr-seo.git"
27
+ "url": "https://github.com/Tonmoy01/react-ssr-seo-toolkit.git"
28
28
  },
29
29
  "homepage": "https://react-ssr-seo.tonmoykhan.site/",
30
30
  "bugs": {
31
- "url": "https://github.com/Tonmoy01/react-ssr-seo/issues"
31
+ "url": "https://github.com/Tonmoy01/react-ssr-seo-toolkit/issues"
32
32
  },
33
33
  "type": "module",
34
34
  "main": "./dist/index.cjs",