routerino 2.6.0 → 2.6.2

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 CHANGED
@@ -1,50 +1,26 @@
1
- # Routerino
2
-
3
- > A lightweight, SEO-optimized React router & SSG for modern websites and applications
4
-
5
- For teams who want SPA simplicity with search-friendly static HTML, Open Graph previews, and **no framework lock-in.**
6
-
7
- Routerino is a zero-dependency router for React designed for optimal SEO performance in client-side rendered applications. Built for modern web architectures like JAMStack applications and Vite-powered React sites, it provides route & meta tag management, sitemap generation, and static site generation or [prerender](https://github.com/prerender/prerender) support to ensure your React applications are fully discoverable by search engines.
8
-
9
- ## Why Routerino?
10
-
11
- - SEO-First Design: Automatic meta tag management, sitemap generation, and prerender support ensure maximum search engine visibility
12
- - Zero Added Dependencies: Keeps bundle size minimal and reduces supply-chain vulnerabilities
13
- - Simple API: No special `Link` components required - use standard HTML anchors and navigate programmatically with standard browser APIs
14
- - Static Site Generation: Build-tool agnostic static HTML generation for improved performance and SEO
15
- - Prerender Ready: Works with prerender servers (such as prerender/prerender) via dedicated meta tags for correct crawler status codes
16
- - Single File Core: The entire routing logic fits in one file, making it easy to understand and customize
17
-
18
- ## Table of Contents
19
-
20
- - [Features](#features)
21
- - [Installation](#installation)
22
- - [Usage](#usage)
23
- - [Props](#props-arguments)
24
- - [useRouterino Hook](#using-the-userouterino-hook)
25
- - [updateHeadTag](#updateheadtag)
26
- - [Image Component](#image-component)
27
- - [Optimization](#optimization)
28
- - [Image Props](#image-props)
29
- - [Image Usage Examples](#image-usage-examples)
30
- - [TypeScript Support](#typescript-support)
31
- - [Best Practices](#routerino-best-practices)
32
- - [Generating a Sitemap](#generating-a-sitemap-from-routes)
33
- - [Static Site Generation](#static-site-generation)
34
- - [How-to Guides & Examples](#how-to-guides--example-code)
35
- - [Starting a New Project](#starting-a-new-react-project-with-routerino)
36
- - [Full React Example](#full-react-example)
37
- - [Basic Example](#basic-example)
38
- - [ErrorBoundary Component](#errorboundary-component)
39
- - [Vendoring Routerino](#vendoring-routerino)
40
- - [Additional Resources](#additional-resources)
41
- - [Contributions](#contributions)
42
- - [License](#license)
1
+ # Routerino — The React router that Google can read.
2
+
3
+ > React routing with built-in SEO. No framework, no lock-in.
4
+
5
+ **Live demos:** [raymondsseptic.com](https://raymondsseptic.com) · [kissimmeekastawayvilla.com](https://kissimmeekastawayvilla.com) · [nerdswithkeyboards.com](https://nerdswithkeyboards.com)· [logbook-ed.com](https://logbook-ed.com)
6
+
7
+ ---
8
+
9
+ ## Why
10
+
11
+ React SPAs are fast for users but invisible to search engines. The standard answer — Next.js or Remix — forces a full framework adoption, new conventions, and vendor lock-in for what should be a solved problem: **generating static HTML with proper meta tags at build time.**
12
+
13
+ Routerino gives you that. Zero added dependencies, standard `<a>` tags, and a Vite plugin that generates complete static HTML including meta tags, Open Graph tags, canonical URLs, sitemap.xml, and robots.txt. You keep your SPA developer experience. Google gets readable pages.
43
14
 
44
15
  ## Quick Start
45
16
 
17
+ ```sh
18
+ npm i routerino
19
+ ```
20
+
46
21
  ```jsx
47
- // export your routes for SSG, not required for CSR-only
22
+ import Routerino from "routerino";
23
+
48
24
  export const routes = [
49
25
  {
50
26
  path: "/",
@@ -69,29 +45,18 @@ export const routes = [
69
45
  <Routerino title="Example.com" routes={routes} />;
70
46
  ```
71
47
 
72
- This simple configuration automatically handles routing, meta tags, and SEO optimization for your React application.
73
-
74
- ## Features
75
-
76
- - Routing
77
- - Easy integration of simple routing for your React app (supports React 18 and 19)
78
- - Zero dependencies for lighter, more maintainable projects
79
- - No special link components required, works great for Markdown-based pages and semantic HTML
80
-
81
- - SEO Optimization
82
- - Configure title, description, and image for each route
83
- - Set `<head>` tags for any route (either directly in your routes config, or dynamically after rendering)
84
- - Set a site-wide name to be included with page titles
85
- - Automatically generate and maintain an up-to-date `sitemap.xml` from your routes
86
- - Generate static HTML files for each route with proper meta tags
87
- - Implement SEO best practices out-of-the-box
88
- - Optimize for Googlebot with pre-rendering support
89
- - Delegate image optimization to `vite-plugin-image-optimizer`
48
+ ## What It Does
90
49
 
91
- - Enhanced User Experience
92
- - Support for sharing and social preview metadata
93
- - Snappy page transitions with automatic scroll reset, eliminating the jarring experience of landing mid-page when navigating
94
- - Hash link support: SPA navigation to `/page#section` correctly scrolls to the target element, mimicking native browser behavior
50
+ | Feature | Routerino | React Router | Next.js | Gatsby | Remix |
51
+ | ---------------------- | ----------------------- | -------------------- | -------------------- | -------------------- | -------------------- |
52
+ | SSG (static HTML) | Built-in Vite plugin | ❌ | Built-in | Core feature | Built-in |
53
+ | Meta tags / Open Graph | Per-route, automatic | ❌ | Metadata API | Via plugin | ✅ meta export |
54
+ | Sitemap generation | ✅ Automatic | ❌ | ❌ Manual | ✅ Via plugin | ❌ Manual |
55
+ | Canonical URLs | ✅ Automatic | ❌ | ❌ Manual | ❌ Manual | ❌ Manual |
56
+ | Standard `<a>` tags | ✅ No `<Link>` needed | ❌ Must use `<Link>` | ❌ Must use `<Link>` | ❌ Must use `<Link>` | ❌ Must use `<Link>` |
57
+ | Runtime dependencies | 0 (peer: react) | 3+ | 50+ | 100+ | 20+ |
58
+ | Prerender support | ✅ Built-in | ❌ | ✅ ISR | ✅ SSG | ✅ Built-in |
59
+ | Library, not framework | ✅ | ✅ | ❌ | ❌ | ❌ |
95
60
 
96
61
  ## Installation
97
62
 
@@ -99,683 +64,94 @@ This simple configuration automatically handles routing, meta tags, and SEO opti
99
64
  npm i routerino
100
65
  ```
101
66
 
102
- Note: Routerino requires React, React DOM, and PropTypes as peer dependencies. These are typically already installed in React projects.
103
-
104
- ### Compatibility
105
-
106
- Routerino supports:
107
-
108
- - React 18 and 19: Both versions are tested and supported. React 17 works for CSR-only.
109
- - Node.js 18+: Tested on Node.js 18, 20, 22, and 24. Could be used on earlier versions if we skip tests.
110
- - Preact: Compatible via `@preact/compat`. See [using Preact](#using-preact) below.
67
+ Routerino requires React, React DOM, and PropTypes as peer dependencies (typically already installed). Supports React 18/19 and Node.js 18+.
111
68
 
112
69
  ## Usage
113
70
 
114
- Here's a short example of using Routerino in your React application:
71
+ ### Route Configuration
72
+
73
+ Routes are plain objects. At minimum, each needs a `path` and `element`:
115
74
 
116
75
  ```jsx
117
76
  export const routes = [
118
77
  {
119
78
  path: "/",
120
- element: <p>This is the home page!</p>,
121
- title: "My Home Page!",
122
- description: "Welcome to my home page!",
79
+ element: <HomePage />,
80
+ title: "Home",
81
+ description: "Welcome to our site",
123
82
  },
124
- ];
125
- <Routerino
126
- title="Example.com"
127
- routes={routes}
128
- debug={window.location.hostname === "localhost"}
129
- />;
130
- ```
131
-
132
- Links are just standard HTML anchor tags. No need to use special `<Link>` components—you can use whatever components or design system you like. For example: <a href="/some-page/">a link</a> is perfectly valid. This is very handy for markdown-based content. With standard link support in Routerino, you won't need to [transform your markdown content with custom React components](https://github.com/remarkjs/react-markdown?tab=readme-ov-file#appendix-b-components).
133
-
134
- Routerino handles same-origin anchor clicks via SPA navigation. The browser handles the rest natively, including:
135
-
136
- - **Cross-origin links** (different domain)
137
- - **Non-HTTP schemes** (mailto:, tel:, javascript:, etc.)
138
- - **Modified clicks** (Ctrl/Cmd+click, Shift+click, Alt+click, right-click)
139
- - **`target="_blank"`** links
140
- - **`download`** attribute links
141
- - **`rel="external"`** links
142
- - **File extension links** — PDFs, ZIPs, images, fonts, videos, JSON, XML, and 50+ other file types are recognized automatically
143
- - **Custom patterns** — use the [`ignorePatterns`](#ignorepatterns-string) prop for additional exclusions (e.g., `/api/`, `/legacy/`)
144
-
145
- ### Programmatic Navigation
146
-
147
- <details>
148
- <summary style="cursor:pointer;font-style:italic">Navigate programmatically using standard browser APIs (expand for details)</summary>
149
-
150
- ```js
151
- // Using History API
152
- window.history.pushState({}, "", "/about/");
153
- window.dispatchEvent(new PopStateEvent("popstate"));
154
-
155
- // Working with URL and search params
156
- const url = new URL(window.location);
157
- url.searchParams.set("page", "2");
158
- window.history.pushState({}, "", url.toString());
159
- window.dispatchEvent(new PopStateEvent("popstate"));
160
- ```
161
-
162
- </details>
163
-
164
- See [props](#props-arguments) for full explanations and [example code](#how-to-guides--example-code) for more complete code samples.
165
-
166
- ### Using Preact
167
-
168
- Routerino is fully compatible with Preact via the `@preact/compat` compatibility layer. This allows you to use Routerino in Preact projects with the same API.
169
-
170
- #### Setup Instructions
171
-
172
- 1. Install Preact and the compatibility layer:
173
-
174
- ```sh
175
- npm i preact @preact/compat
176
- ```
177
-
178
- 2. Configure your bundler to alias React to @preact/compat:
179
-
180
- **Vite Configuration:**
181
-
182
- ```js
183
- // vite.config.js
184
- import { defineConfig } from "vite";
185
- import preact from "@preact/preset-vite";
186
-
187
- export default defineConfig({
188
- plugins: [preact()],
189
- resolve: {
190
- alias: {
191
- react: "@preact/compat",
192
- "react-dom": "@preact/compat",
193
- "react/jsx-runtime": "@preact/compat/jsx-runtime",
194
- },
83
+ {
84
+ path: "/about/",
85
+ element: <AboutPage />,
86
+ title: "About Us",
87
+ description: "Learn more about our team",
88
+ imageUrl: "/images/about-og.jpg",
195
89
  },
196
- });
197
- ```
198
-
199
- **Webpack Configuration:**
200
-
201
- ```js
202
- // webpack.config.js
203
- module.exports = {
204
- resolve: {
205
- alias: {
206
- react: "preact/compat",
207
- "react-dom": "preact/compat",
208
- },
90
+ {
91
+ path: "/products/:id/", // dynamic route — not statically generated
92
+ element: <ProductPage />,
209
93
  },
210
- };
211
- ```
212
-
213
- 3. Use Routerino exactly as you would in a React project - the API is identical!
214
-
215
- ### Props (arguments)
216
-
217
- The table below shows all available props with their default values. See the [usage](#usage) section for examples.
218
-
219
- #### Routerino props
220
-
221
- All of these are optional, so it's easy to get started with nothing but a bare-bones `<Routerino />` element, to get started with a working sample page. The main props you'll need are `routes` and `title`. See [RouteConfig props](#routeconfig-props) for the route format.
222
-
223
- | Prop | Type | Description | Default |
224
- | ---------------------------------------------- | --------------- | --------------------------------- | ----------------------------- |
225
- | [title](#title-string) | string | The site title | `""` |
226
- | [routes](#routes-array-of-routeconfig-objects) | RouteConfig[] | Array of route configurations | `[Default Route Object]` |
227
- | [separator](#separator-string) | string | Title separator | `" \| "` |
228
- | [notFoundTemplate](#notfoundtemplate-element) | React.ReactNode | 404 page template | `<DefaultNotFoundTemplate />` |
229
- | [notFoundTitle](#notfoundtitle-string) | string | 404 page title | `"Page not found [404]"` |
230
- | [errorTemplate](#errortemplate-element) | React.ReactNode | Error page template | `<DefaultErrorTemplate />` |
231
- | [errorTitle](#errortitle-string) | string | Error page title | `"Page error [500]"` |
232
- | [useTrailingSlash](#usetrailingslash-bool) | boolean | Use trailing slashes in URLs | `true` |
233
- | [usePrerenderTags](#useprerendertags-bool) | boolean | Use pre-render meta tags | `false` |
234
- | [baseUrl](#baseurl-string) | string | Base URL for canonical tags | `null` (uses window.location) |
235
- | [imageUrl](#imageurl-string) | string | Default image URL for sharing | `null` |
236
- | [touchIconUrl](#touchiconurl-string) | string | Image URL for PWA homescreen icon | `null` |
237
- | [debug](#debug-boolean) | boolean | Enable debug mode | `false` |
238
- | [ignorePatterns](#ignorepatterns-string) | string[] | URL patterns to skip SPA routing | `[]` |
239
-
240
- ##### `title`: string;
241
-
242
- The site title, such as "Foo.com". This will be appended to your page's title with a default separator. For example: "Page Title | Foo.com"
243
-
244
- ##### `routes`: array of `RouteConfig` objects;
245
-
246
- See [RouteConfig props](#routeconfig-props) for more details. At a minimum a path and React element are required for each route.
247
-
248
- - path: string;
249
- - element: React.ReactNode;
250
- - title?: string;
251
- - description?: string;
252
- - tags?: HeadTag[];
253
- - imageUrl?: string;
254
-
255
- ##### `separator`: string;
256
-
257
- A string to separate the page title from the site title. The default is `" | "` (a pipe character w/space around). Set this to customize the separator.
258
-
259
- ##### `notFoundTemplate`: element;
260
-
261
- Any React element for the default (or no) route match.
262
-
263
- Default:
264
-
265
- ```jsx
266
- <>
267
- <p>No page found for this URL. [404]</p>
268
- <p>
269
- <a href="/">Home</a>
270
- </p>
271
- </>
272
- ```
273
-
274
- ##### `notFoundTitle`: string;
275
-
276
- A title string for no route match.
277
-
278
- Default: `"Page not found [404]"`
279
-
280
- ##### `errorTemplate`: element;
281
-
282
- Any React element for uncaught exceptions.
283
-
284
- Default:
285
-
286
- ```jsx
287
- <>
288
- <p>Page failed to load. [500]</p>
289
- <p>
290
- <a href="/">Home</a>
291
- </p>
292
- </>
293
- ```
294
-
295
- ##### `errorTitle`: string;
296
-
297
- A title string for uncaught exceptions.
298
-
299
- Default: `"Page error [500]"`
300
-
301
- ##### `useTrailingSlash`: bool;
302
-
303
- Use trailing slashes as the canonical URL. See best practices section for an explanation.
304
-
305
- Default: `true`
306
-
307
- ##### `usePrerenderTags`: bool;
308
-
309
- Include prerender-specific meta tags to enable proper error codes like 404 when serving prerendered pages to a search crawler. This means that Routerino uses <meta name="prerender-status-code" content="..."> and <meta name="prerender-header" content="Location: ..."> to signal status codes and redirects to prerender servers.
310
-
311
- Default: `false`
312
-
313
- ##### `baseUrl`: string;
314
-
315
- The base URL to use for canonical tags and og:url meta tags. If not provided, uses `window.location.origin`.
316
-
317
- This is useful when your site is accessible from multiple domains (for example, both example.com and example.net point to the same app). Set it to the preferred domain and all canonical and og:url tags will consistently point there. Also helpful for pinning to the production URL in development or staging environments.
318
-
319
- **Important:** The baseUrl must NOT end with a trailing slash (`/`). Correct example: `"https://example.com"`
320
-
321
- Default: `null` (uses window.location.origin)
322
-
323
- ##### `imageUrl`: string;
324
-
325
- A string containing the path of the default (site-wide) image to use for sharing previews.
326
-
327
- Default: `null`
328
-
329
- ##### `touchIconUrl`: string;
330
-
331
- A string containing the path of the image to use for PWA homescreen icon.
332
-
333
- Default: `null`
334
-
335
- ##### `debug`: boolean;
336
-
337
- Enable debug mode for additional logging and information. When enabled, Routerino logs detailed information to the console including:
338
-
339
- - Route changes and pattern matching
340
- - Meta tag updates
341
- - Error boundaries and component failures
342
- - Performance timing information
343
-
344
- Example debug output:
345
-
346
- ```
347
- [Routerino] Route changed to: /products/laptop/
348
- [Routerino] Matched pattern: /products/:id/
349
- [Routerino] Route params: { id: "laptop" }
350
- [Routerino] Updated meta tag: og:title = "Laptop Pro - $1299"
351
- ```
352
-
353
- Default: `false`
354
-
355
- ##### `ignorePatterns`: string[];
356
-
357
- An array of regex pattern strings to match against link hrefs. Any same-origin link matching one of these patterns will **not** be intercepted by the SPA router — the browser will handle the navigation natively instead.
358
-
359
- Patterns are tested case-insensitively against the full resolved href (including origin).
360
-
361
- ```jsx
362
- // Skip API endpoints and a legacy section
363
- <Routerino routes={routes} ignorePatterns={["/api/", "/admin/legacy/"]} />
364
- ```
365
-
366
- Routerino also has a built-in list of file extensions that are automatically skipped: `.pdf`, `.zip`, `.docx`, `.png`, `.jpg`, `.svg`, `.mp4`, `.json`, `.xml`, `.csv`, `.txt`, `.ico`, `.woff2`, `.woff`, `.ttf`, `.mp3`, `.wav`, `.epub`, `.map`, `.css`, `.js`, `.wasm`, and many more. The `ignorePatterns` prop is for additional custom patterns beyond these defaults.
367
-
368
- Default: `[]`
369
-
370
- #### RouteConfig props
371
-
372
- There is a default RouteConfig that will be loaded if you don't specify any routes. The default route is a basic template that can confirm your app is working and routing is good to go.
373
-
374
- ##### path: string;
375
-
376
- The path of the desired route. **Must start with a forward slash (`/`)**. For example: `"/foo/"`, or `"/about/"`. Supported dynamic segments use the :param syntax (e.g., /products/:id/). Wildcards, optional segments, and splats are not supported. Params are matched by position; decode values in your components if needed.
377
-
378
- ##### element: React.ReactNode;
379
-
380
- The React element to be rendered at the desired route, for example: `<FooPage />`
381
-
382
- ##### title?: string;
383
-
384
- The page's title, which should not include the site's title. For example: `"Contact Us"`
385
-
386
- ##### description?: string;
387
-
388
- The page's description, which will show up on search results pages.
389
-
390
- ##### tags?: HeadTag[];
391
-
392
- Any desired head tags for that route. See [HeadTag props](#headtag-props) for details.
393
-
394
- ##### imageUrl?: string;
395
-
396
- An image URL to set for the page's og:image tag.
397
-
398
- #### HeadTag props
399
-
400
- An array of HeadTag objects that can be added to the route to manage meta tags, links, and other elements in the `<head>` section of the HTML document. These HeadTag objects are processed by the `updateHeadTag` function to update or create a `<head>` child tag (usually `<meta>` tags). The available props and most common tag attributes are listed below, but any arbitrary tag attributes are supported. See the [updateHeadTag section](#updateheadtag) for more details.
401
-
402
- - `tag` (string, default: "meta"): The HTML element to update or create. By default, it is set to "meta", but you can specify other tags like "link" or "title".
403
-
404
- - `soft` (boolean, default: false): When set to `true`, it prevents overwriting the value of an existing tag if it already exists. This is useful when you want to preserve existing tag attributes.
405
-
406
- - `name` (string): The "name" attribute of the tag. Commonly used for meta tags to specify the name of the metadata.
407
-
408
- - `property` (string): The "property" attribute of the tag. Used for Open Graph (OG) meta tags to define specific OG properties.
409
-
410
- - `content` (string): The "content" attribute of the tag. Specifies the value or content of the metadata. Sometimes two distinct meta tags may share identical content, so it's not used for matching.
411
-
412
- - `charset` (string): The "charset" attribute of the tag. Defines the character encoding for the document.
413
-
414
- - `httpEquiv` (string): The "http-equiv" attribute of the tag. Used for defining HTTP headers for the document.
415
-
416
- - `itemProp` (string): The "itemProp" attribute of the tag. Used for adding schema.org microdata to the tag.
417
-
418
- - `rel` (string): The "rel" attribute of the tag. Specifies the relationship between the current document and the linked resource.
419
-
420
- - `href` (string): The "href" attribute of the tag. Specifies the URL of the linked resource.
421
-
422
- - `src` (string): The "src" attribute of the tag. Specifies the URL of an external resource, such as an image or script.
423
-
424
- - `sizes` (string): The "sizes" attribute of the tag. Defines the sizes of the linked resource, commonly used for favicon links.
425
-
426
- - `type` (string): The "type" attribute of the tag. Specifies the MIME type of the linked resource.
427
-
428
- - `media` (string): The "media" attribute of the tag. Defines the media or device the linked resource is optimized for.
429
-
430
- - `hrefLang` (string): The "hrefLang" attribute of the tag. Specifies the language of the linked resource.
431
-
432
- - `target` (string): The "target" attribute of the tag. Defines where to open the linked resource.
433
-
434
- - `innerHTML` (string): Inner HTML content for tags that require a closing tag, such as `<script>` and `<style>`. When provided, the tag is rendered as `<tag attrs>innerHTML</tag>` instead of a self-closing element. This enables structured data (JSON-LD), inline CSS, and other tag-body content.
435
-
436
- ##### Structured Data Example
437
-
438
- ```jsx
439
- {
440
- path: "/about/",
441
- element: <AboutPage />,
442
- tags: [{
443
- tag: "script",
444
- type: "application/ld+json",
445
- innerHTML: JSON.stringify({
446
- "@context": "https://schema.org",
447
- "@type": "BreadcrumbList",
448
- itemListElement: [
449
- { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
450
- { "@type": "ListItem", "position": 2, "name": "About", "item": "https://example.com/about/" },
451
- ],
452
- }),
453
- }],
454
- }
94
+ ];
455
95
  ```
456
96
 
457
- This works both at runtime (via `updateHeadTag`) and during static site generation (via the forge plugin).
97
+ Links are standard `<a>` tags. Routerino intercepts same-origin clicks for SPA navigation; the browser handles everything else (cross-origin links, `mailto:`, `target="_blank"`, file downloads, etc.) automatically.
458
98
 
459
- ### Using the `useRouterino` Hook
99
+ ### The `useRouterino` Hook
460
100
 
461
- The `useRouterino` hook provides access to router state from any child component. This is the primary way to access route information and update head tags dynamically.
101
+ Access router state from any component:
462
102
 
463
103
  ```jsx
464
104
  import { useRouterino } from "routerino";
465
105
 
466
- function MyComponent() {
106
+ function ProductPage() {
467
107
  const { currentRoute, params, routePattern, updateHeadTag } = useRouterino();
468
108
 
469
- // Access current route information
470
- console.log("Current path:", currentRoute); // e.g., "/products/laptop/"
471
- console.log("Route pattern:", routePattern); // e.g., "/products/:id/"
472
- console.log("Route params:", params); // e.g., { id: "laptop" }
473
-
474
- // Update meta tags dynamically
475
109
  useEffect(() => {
476
- updateHeadTag({
477
- name: "description",
478
- content: `Product page for ${params.id}`,
479
- });
110
+ updateHeadTag({ name: "description", content: `Product: ${params.id}` });
480
111
  }, [params.id]);
481
112
 
482
113
  return <div>Product: {params.id}</div>;
483
114
  }
484
115
  ```
485
116
 
486
- #### Hook Return Values
487
-
488
- - **`currentRoute`**: The current URL path (e.g., `/foo/bar/`)
489
- - **`routePattern`**: The matched route pattern with parameters (e.g., `/foo/:id/`)
490
- - **`params`**: Object containing route parameters (e.g., `{id: "bar"}`)
491
- - **`updateHeadTag`**: Function to dynamically update head tags (see [updateHeadTag](#updateheadtag) section)
117
+ **Returns:** `currentRoute`, `params`, `routePattern`, `updateHeadTag`
492
118
 
493
119
  ### `updateHeadTag`
494
120
 
495
- The `updateHeadTag` function is responsible for creating or updating the specified head tag. It searches for an existing tag based on the provided attributes (excluding the "content" attribute) to prevent duplicate tags. If a matching tag is found (and the `soft` prop is not set to `true`), the function updates the tag's attributes. If no matching tag is found, a new tag is created with the specified attributes.
496
-
497
- Please note that the `updateHeadTag` function requires at least one attribute to be provided. If no attributes are specified, an error message will be logged.
498
-
499
- #### Props
500
-
501
- See [HeadTag props](#headtag-props) for arguments and some common tag attributes.
502
-
503
- ## TypeScript Support
504
-
505
- Routerino includes TypeScript definitions out of the box. The types are automatically available when you import Routerino.
506
-
507
- ### Basic Usage
508
-
509
- ```typescript
510
- // Both import styles are supported
511
- import Routerino from 'routerino'; // Default import
512
- // or
513
- import { Routerino } from 'routerino'; // Named import (recommended)
514
-
515
- import type { RouteConfig } from 'routerino';
516
-
517
- export const routes: RouteConfig[] = [
518
- {
519
- path: '/',
520
- element: <HomePage />,
521
- title: 'Home',
522
- description: 'Welcome to our site'
523
- }
524
- ];
525
-
526
- // TypeScript will validate all props
527
- <Routerino
528
- routes={routes}
529
- baseUrl="https://example.com"
530
- title="My Site"
531
- />
532
- ```
533
-
534
- ## Examples
535
-
536
- Setting a page description:
121
+ Create or update head tags at any time. Matches existing tags by attribute to avoid duplicates:
537
122
 
538
123
  ```js
539
124
  updateHeadTag({ name: "description", content: "Some description..." });
125
+ updateHeadTag({ tag: "link", rel: "apple-touch-icon", href: "/icon.png" });
126
+ updateHeadTag({ property: "og:site_name", content: "Your Brand" });
540
127
  ```
541
128
 
542
- Adding an apple touch icon (for when saving to the home screen):
543
-
544
- ```js
545
- updateHeadTag({
546
- tag: "link",
547
- rel: "apple-touch-icon",
548
- href: "/example-icon.png",
549
- });
550
- ```
551
-
552
- While these two examples are automatically handled for you via the `description` and `touchIconUrl` properties in [RouteConfig props](#routeconfig-props), you might want to update them later or in specific scenarios.
553
-
554
- ## Routerino Best Practices
555
-
556
- To optimize your site for SEO and social previews when using Routerino, consider the following best practices:
557
-
558
- ### Page Titles
559
-
560
- - Keep page titles unique for each route. Avoid including the site name like "Foo.com" in individual page titles, Routerino adds that automatically.
561
- - Aim for concise, descriptive titles that accurately represent the page content.
562
- - We prefer to keep title length at a max of 50-60 characters, longer text will be ignored or cut off (especially for mobile users).
563
-
564
- ### URL Structure & Canonicalization
565
-
566
- #### Canonical URLs (Don't worry, this is handled for you!)
567
-
568
- **What is canonicalization?**
569
-
570
- When multiple URLs show the same content (like `/about` vs `/about/`), search engines need to know which one is the "official" version to avoid duplicate content penalties. The canonical URL tells search engines which version to index and rank.
571
-
572
- **How Routerino handles this automatically**
573
-
574
- - Sets `<link rel="canonical">` tags on every page pointing to the preferred URL version
575
- - Uses the `useTrailingSlash` prop (default: `true`) to determine the canonical format
576
- - Generates proper `og:url` meta tags for social sharing
577
- - For SSG: Creates both file versions (`/about.html` and `/about/index.html`) with canonical tags
578
- - For Prerender: Includes meta tags that instruct the prerender server to return 301 redirects
579
- - Ensures sitemap.xml only contains canonical URLs
580
-
581
- **Best practices that Routerino implements**
582
-
583
- - Consistency: Sitemap, canonical, and redirects all agree
584
- - Dual file generation (SSG): Creates both `/about.html` and `/about/index.html` so both URLs work
585
- - Prerender support (SPA): Includes meta tags that tell prerender services to serve proper status codes
586
- - Absolute URLs: When `baseUrl` is provided, canonical tags use the provided base instead of defaulting to the current domain
587
-
588
- **Example of what's generated**
589
-
590
- With Static Site Generation (SSG):
591
-
592
- ```html
593
- <!-- Both /about.html and /about/index.html contain: -->
594
- <link rel="canonical" href="https://example.com/about/" />
595
- <meta property="og:url" content="https://example.com/about/" />
596
- ```
597
-
598
- With Prerender (SPA):
599
-
600
- ```html
601
- <!-- For canonical URL (/about/) -->
602
- <link rel="canonical" href="https://example.com/about/" />
603
- <meta property="og:url" content="https://example.com/about/" />
604
-
605
- <!-- For non-canonical URL (/about) -->
606
- <meta name="prerender-status-code" content="301" />
607
- <meta name="prerender-header" content="Location: https://example.com/about/" />
608
- ```
609
-
610
- You can override the default with `useTrailingSlash={false}` if you prefer URLs without trailing slashes. Either way, Routerino ensures search engines see consistent, canonical URLs.
611
-
612
- **Multi-domain deployments**: If your site is served from multiple domains, set `baseUrl` to the preferred domain to keep all canonical and og:url tags pointing to one place. For example, `<Routerino baseUrl="https://example.com" routes={routes} />` ensures search engines see one canonical domain even when the same page is reachable at example.net.
613
-
614
- ### Sitemap Generation
615
-
616
- - Automate the creation of `sitemap.xml` and `robots.txt` during your build process with Routerino Forge.
617
- - The Vite plugin automatically generates these files when building static sites (see [Static Site Generation](#static-site-generation)).
618
-
619
- ### Social Previews & Open Graph
620
-
621
- Routerino automatically sets the core Open Graph tags (`og:title`, `og:description`, `og:url`, `og:image`) for every page. Here's how to optimize them:
622
-
623
- #### Image Best Practices
624
-
625
- - Size: Use 1200×630 pixels (1.91:1 ratio) for maximum compatibility
626
- - Content: Avoid text in images - use metadata for text instead (per Apple's guidelines)
627
- - Add dimensions for faster first-share rendering:
628
- ```js
629
- // In your route's component or after data loading
630
- updateHeadTag({ property: "og:image:width", content: "1200" });
631
- updateHeadTag({ property: "og:image:height", content: "630" });
632
- ```
633
-
634
- #### Title & Branding
635
-
636
- - Don't duplicate branding in `og:title` - Routerino uses your page title directly
637
- - For site-wide branding, add `og:site_name` instead:
638
- ```js
639
- updateHeadTag({ property: "og:site_name", content: "Your Brand" });
640
- ```
641
-
642
- #### Platform-Specific Enhancements
643
-
644
- - Apple/iMessage: Set `touchIconUrl` prop for iMessage link previews
645
- - Video content: Add direct playable assets when possible:
646
- ```js
647
- updateHeadTag({
648
- property: "og:video",
649
- content: "https://example.com/video.mp4",
650
- });
651
- updateHeadTag({ property: "og:video:type", content: "video/mp4" });
652
- ```
653
-
654
- #### Testing Your Previews
655
-
656
- Test how your links appear on different platforms:
657
-
658
- - [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/) - Use "Scrape Again" after changes
659
- - [LinkedIn Post Inspector](https://www.linkedin.com/post-inspector/)
660
- - [Twitter Card Validator](https://cards-dev.twitter.com/validator)
661
-
662
- **Tip**: Social platforms cache previews aggressively. After updating tags, use each platform's debugger to force a refresh.
663
-
664
- #### Twitter Card Tags (Handled Automatically)
665
-
666
- Routerino automatically includes Twitter card meta tags to ensure rich previews when your links are shared on Twitter/X:
667
-
668
- ```html
669
- <meta name="twitter:card" content="summary_large_image" />
670
- ```
671
-
672
- **What this does:**
673
-
674
- - Without Twitter cards: Links appear as plain URLs with no preview
675
- - With Twitter cards: Links show rich previews with title, description, and image
676
-
677
- **How it works:**
678
-
679
- - Routerino always sets `summary_large_image` for maximum engagement
680
- - If you provide an `imageUrl`, Twitter displays a large prominent image
681
- - If no image is provided, Twitter gracefully falls back to a text-only card
682
- - This is better than no card at all (which would show just the bare URL)
683
-
684
- **The complete picture:**
685
-
686
- ```html
687
- <!-- What Routerino generates for social sharing -->
688
- <meta name="twitter:card" content="summary_large_image" />
689
- <meta property="og:title" content="Your Page Title" />
690
- <meta property="og:description" content="Your page description" />
691
- <meta property="og:image" content="https://example.com/preview.jpg" />
692
- ```
693
-
694
- This creates an engaging Twitter preview with a large image, title, and description - much more clickable than a plain URL.
695
-
696
- ### Meta Descriptions
697
-
698
- - Provide unique, informative descriptions for each route.
699
- - Descriptions may be used for snippets so keep them short and to the point.
700
- - Descriptions longer than ~150 characters may be truncated in search results.
701
-
702
- ### Additional SEO Considerations
703
-
704
- - Use semantic HTML elements in your components for better content structure.
705
- - Implement structured data (JSON-LD) where applicable to enhance rich snippets in search results. Use the `innerHTML` property on a `<script type="application/ld+json">` head tag (see the [Structured Data Example](#structured-data-example) above).
706
- - Ensure your site is mobile-friendly and loads quickly for better search engine rankings.
707
-
708
- By following these practices, you'll improve your site's SEO performance and social media presence when using Routerino.
709
-
710
- ### Hash Links
711
-
712
- Routerino supports standard `<a href="/page#section">` links for SPA navigation. After React renders the new page, it finds the element with the matching `id` and scrolls it into view.
713
-
714
- **Sticky headers**: If your site has a fixed header, use the CSS [`scroll-margin-top`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top) property to offset the scroll target so content isn't hidden behind the header:
715
-
716
- ```css
717
- [id] {
718
- scroll-margin-top: 80px; /* match your header height */
719
- }
720
- ```
721
-
722
- This works for SPA navigation, native browser hash navigation, and direct page loads — no extra props or router changes needed.
723
-
724
- ## Sitemap and robots.txt Generation
725
-
726
- When using Routerino Forge for static site generation, `sitemap.xml` and `robots.txt` files are automatically generated during the build process:
727
-
728
- - sitemap.xml: Contains all static routes (dynamic routes with parameters like `:id` are excluded)
729
- - robots.txt: Created with a reference to the sitemap (if it doesn't already exist)
730
-
731
- These files are generated automatically when you build with the Routerino Forge Vite plugin - no additional configuration needed.
732
-
733
- ### Sample Build Output:
129
+ Set `soft: true` to skip overwriting existing values. Use `innerHTML` for structured data and non-self-closing tags. See [docs/seo-guide.md](docs/seo-guide.md) for full details.
734
130
 
735
- ```
736
- ✓ Generated sitemap.xml with 42 URLs
737
- ✓ Generated robots.txt
738
- ✓ Generated 42 static pages + 404.html
739
- ```
740
-
741
- ## Image Optimization
742
-
743
- Routerino delegates image optimization to [`vite-plugin-image-optimizer`](https://github.com/FatehAK/vite-plugin-image-optimizer). Install it and add it to your Vite config **before** `routerinoForge`:
744
-
745
- ```bash
746
- npm install --save-dev vite-plugin-image-optimizer sharp
747
- ```
131
+ ### Programmatic Navigation
748
132
 
749
133
  ```js
750
- // vite.config.js
751
- import { defineConfig } from "vite";
752
- import react from "@vitejs/plugin-react";
753
- import { ViteImageOptimizer } from "vite-plugin-image-optimizer";
754
- import { routerinoForge } from "routerino/forge";
755
-
756
- export default defineConfig({
757
- plugins: [
758
- react(),
759
- ViteImageOptimizer({
760
- jpg: { quality: 80 },
761
- jpeg: { quality: 80 },
762
- png: { quality: 80 },
763
- webp: { quality: 80 },
764
- }),
765
- routerinoForge({ baseUrl: "https://example.com" }),
766
- ],
767
- });
134
+ window.history.pushState({}, "", "/about/");
135
+ window.dispatchEvent(new PopStateEvent("popstate"));
768
136
  ```
769
137
 
770
- Use standard HTML `<img>` tags in your components — the plugin optimizes source images in `public/` and imported assets during the Vite build. No special component or props needed.
138
+ ### ErrorBoundary
771
139
 
772
- ## Static Site Generation
140
+ ```jsx
141
+ import { ErrorBoundary } from "routerino";
773
142
 
774
- Routerino provides static site generation (SSG), creating fully-rendered HTML files at build time with "just clean JSX elements that render identically on server and client."
143
+ <ErrorBoundary
144
+ fallback={<div>Something went wrong.</div>}
145
+ errorTitleString="Error | My Site"
146
+ debug={window.location.hostname === "localhost"}
147
+ >
148
+ <MyComponent />
149
+ </ErrorBoundary>;
150
+ ```
775
151
 
776
- ### Routerino Forge - Vite Plugin for SSG
152
+ ## Static Site Generation (Routerino Forge)
777
153
 
778
- The Vite plugin generates static HTML files with your React components fully rendered at build time. Zero configuration required - the plugin automatically handles all the complexity!
154
+ Add the Vite plugin, export your routes, and build:
779
155
 
780
156
  ```js
781
157
  // vite.config.js
@@ -787,15 +163,7 @@ export default defineConfig({
787
163
  plugins: [
788
164
  react(),
789
165
  routerinoForge({
790
- baseUrl: "https://example.com", // Your production URL (no trailing slash)
791
- // Optional settings (these are the defaults):
792
- // routes: "./src/routes.jsx", // Your routes file (and App.jsx for full layout)
793
- // outputDir: "dist",
794
- // generateSitemap: true,
795
- // useTrailingSlash: true, // Set to false for /about instead of /about/
796
- // verbose: false,
797
- // ssgCacheDir: "node_modules/.cache/routerino-forge", // SSG cache directory
798
- // verbose: false,
166
+ baseUrl: "https://example.com", // required no trailing slash
799
167
  }),
800
168
  ],
801
169
  });
@@ -803,517 +171,116 @@ export default defineConfig({
803
171
 
804
172
  **Requirements:**
805
173
 
806
- - Your `index.html` must have `<div id="root"></div>` (standard for all React apps)
807
- - **IMPORTANT**: Routes must be exported (not defined inline) for SSG to work
808
-
809
- **Features:**
810
-
811
- - Renders your components to HTML at build time (SSG)
812
- - Generates dual files for maximum URL compatibility:
813
- - `/about` → `about.html`
814
- - `/about/` → `about/index.html`
815
- - Canonical URLs and redirects based on `useTrailingSlash` setting
816
- - Prerender compatible 301 redirects for non-canonical versions
817
- - **Static host ready**: Output format aligns perfectly with any static host such as Netlify and Cloudflare Pages
818
- - Routes generate `/path/index.html` for clean URLs (and `/path.html` for compatibility with no-slash URLs)
819
- - `404.html` at root for custom error pages
820
- - No server configuration needed - just deploy!
821
- - Automatic canonical URL and og:url meta tags
822
- - Injects rendered HTML into your root div
823
- - Generates sitemap.xml and robots.txt
824
- - Creates a 404.html page
825
- - Skips dynamic routes (with `:param` syntax)
826
- - SEO optimized: Complete HTML with meta tags
827
- - Image optimization: Automatic responsive variants + blur placeholders (LQIP) — now delegated to [`vite-plugin-image-optimizer`](https://github.com/FatehAK/vite-plugin-image-optimizer)
828
- - Easy configuration: Works out of the box with Vite and minimal setup
829
-
830
- #### Routes Configuration
831
-
832
- **Critical for SSG**: Routes MUST be exported for the build plugin to discover them. The plugin needs to import your routes at build time, so inline route definitions won't work.
833
-
834
- To include your full layout (headers, footers, etc.) in static generated HTML, export routes from the same file as your App component:
835
-
836
- ```jsx
837
- // App.jsx - export routes from here for full layout SSG
838
- export const routes = [
839
- { path: "/", element: <HomePage />, title: "Home" },
840
- { path: "/about/", element: <AboutPage />, title: "About" },
841
- ];
174
+ - `index.html` must have `<div id="root"></div>`
175
+ - Routes must be **exported** (not defined inline) for the plugin to find them
176
+ - Dynamic routes (with `:param`) are automatically skipped
842
177
 
843
- export default function App() {
844
- return (
845
- <main>
846
- <Header />
847
- <Routerino routes={routes} />
848
- <Footer />
849
- </main>
850
- );
851
- }
852
- ```
178
+ **What you get at build time:**
853
179
 
854
- This ensures your entire App layout is rendered during static site generation. Without this, SSG renders only the individual route elements.
180
+ - Static HTML for every route with full meta tags
181
+ - Dual file generation (`/about.html` + `/about/index.html`) for URL compatibility
182
+ - Automatic `sitemap.xml` and `robots.txt`
183
+ - `404.html` at root for custom error pages
184
+ - Canonical URL and `og:url` meta tags on every page
855
185
 
856
- Define routes with an `element` property containing JSX elements:
186
+ ### Generating Routes from Data
857
187
 
858
188
  ```jsx
859
- export const routes = [
860
- {
861
- path: "/",
862
- element: <HomePage featured="Latest News" />, // Props preserved
863
- title: "Home - My Site",
864
- description: "Welcome to our site",
865
- imageUrl: "/images/home-og.jpg", // Optional social image
866
- },
867
- {
868
- path: "/about/",
869
- element: <AboutPage />,
870
- title: "About Us",
871
- description: "Learn more about our team",
872
- },
873
- {
874
- path: "/products/:id", // Dynamic route - won't be statically generated
875
- element: <ProductDetail />,
876
- title: "Product Detail",
877
- },
878
- ];
879
-
880
- // Optional: Custom 404 page
881
- export const notFoundTemplate = (
882
- <div>
883
- <h1>404 - Page Not Found</h1>
884
- <p>Sorry, the page you're looking for doesn't exist.</p>
885
- </div>
189
+ // Build-time data fetching is fully supported
190
+ const blogPosts = await fetch("https://api.example.com/posts").then((r) =>
191
+ r.json()
886
192
  );
887
- ```
888
-
889
- #### Generating Routes from Data (e.g., Product Listings)
890
-
891
- You can dynamically generate routes from data sources at build time. This is perfect for creating SEO-friendly static pages for products, blog posts, or any content from APIs or databases:
892
-
893
- ```jsx
894
- // src/routes.jsx
895
- import { ProductPage } from "./components/ProductPage";
896
- import { BlogPost } from "./components/BlogPost";
897
-
898
- // This could be fetched from an API at build time
899
- const products = [
900
- {
901
- id: "laptop-pro",
902
- name: "Laptop Pro",
903
- price: 1299,
904
- description: "High-performance laptop",
905
- },
906
- {
907
- id: "wireless-mouse",
908
- name: "Wireless Mouse",
909
- price: 49,
910
- description: "Ergonomic wireless mouse",
911
- },
912
- { id: "usb-hub", name: "USB Hub", price: 29, description: "7-port USB hub" },
913
- ];
914
-
915
- // Generate a route for each product
916
- const productRoutes = products.map((product) => ({
917
- path: `/products/${product.id}/`,
918
- element: <ProductPage product={product} />,
919
- title: `${product.name} - $${product.price}`,
920
- description: product.description,
921
- tags: [
922
- { property: "og:type", content: "product" },
923
- { property: "product:price:amount", content: product.price.toString() },
924
- { property: "product:price:currency", content: "USD" },
925
- ],
926
- }));
927
-
928
- // You can also fetch data asynchronously at build time
929
- const blogPosts = await fetch("https://api.example.com/posts")
930
- .then((res) => res.json())
931
- .catch(() => []); // Fallback to empty array if API fails
932
-
933
- const blogRoutes = blogPosts.map((post) => ({
934
- path: `/blog/${post.slug}/`,
935
- element: <BlogPost post={post} />,
936
- title: post.title,
937
- description: post.excerpt,
938
- imageUrl: post.featuredImage,
939
- tags: [
940
- { property: "og:type", content: "article" },
941
- { property: "article:published_time", content: post.publishedAt },
942
- { property: "article:author", content: post.author },
943
- ],
944
- }));
945
193
 
946
- // Combine all routes
947
194
  export const routes = [
948
- {
949
- path: "/",
950
- element: <HomePage />,
951
- title: "Home",
952
- description: "Welcome to our store",
953
- },
954
- ...productRoutes, // All products get their own static pages
955
- ...blogRoutes, // All blog posts get their own static pages
956
- {
957
- path: "/products/",
958
- element: <ProductListing products={products} />,
959
- title: "All Products",
960
- description: "Browse our product catalog",
961
- },
962
- ];
963
- ```
964
-
965
- **Benefits of this approach:**
966
-
967
- - Each product/post gets its own static HTML page with proper SEO meta tags
968
- - All generated routes are automatically included in `sitemap.xml`
969
- - Search engines can discover and index every product/post
970
- - Fast page loads since HTML is pre-rendered at build time
971
- - Type-safe if using TypeScript with your data structures
972
-
973
- ### What Gets Generated
974
-
975
- The static build process will:
976
-
977
- - Generate HTML files for each static route (e.g., `/about/` → `about/index.html` & `about.html`)
978
- - Skip dynamic routes with parameters (e.g., `/user/:id`)
979
- - Apply route-specific meta tags (title, description, og:image)
980
- - Generate `sitemap.xml` with all static routes (Vite plugin only)
981
- - Preserve your existing HTML structure and assets
982
-
983
- ### Example Output
984
-
985
- For a route configuration like:
986
-
987
- ```javascript
988
- {
989
- path: '/about/',
990
- title: 'About Us',
991
- description: 'Learn more about our company',
992
- imageUrl: 'https://example.com/about-og.jpg'
993
- }
994
- ```
995
-
996
- The generated `/about/index.html` will include:
997
-
998
- ```html
999
- <title>About Us</title>
1000
- <meta name="description" content="Learn more about our company" />
1001
- <meta property="og:title" content="About Us" />
1002
- <meta property="og:description" content="Learn more about our company" />
1003
- <meta property="og:image" content="https://example.com/about-og.jpg" />
1004
- <meta property="og:url" content="https://example.com/about/" />
1005
- ```
1006
-
1007
- This provides excellent SEO while maintaining the benefits of a React SPA.
1008
-
1009
- ## How-to Guides & Example Code
1010
-
1011
- 1. [Starting a New React Project with Routerino](#starting-a-new-react-project-with-routerino)
1012
- 2. [Full React Example](#full-react-example)
1013
- 3. [Basic Example](#basic-example)
1014
-
1015
- ### Starting a New React Project with Routerino
1016
-
1017
- If you're starting from scratch and wondering "How do I create a React project with Routerino?", here's a recommended approach:
1018
-
1019
- 1. First, ensure you have [Node.js](https://nodejs.org/) and npm (Node Package Manager) installed on your system. If you're just getting started, consider using a Node version manager like [Volta](https://volta.sh/), [fnm](https://github.com/Schniz/fnm), or [asdf](https://asdf-vm.com/) for easy installation and management of Node.js versions.
1020
-
1021
- 2. We recommend using [Vite](https://vitejs.dev/) for a fast and lean development experience. Vite is a modern build tool that focuses on speed and simplicity. To create a new React project with Vite, run the following command in your terminal:
1022
-
1023
- ```
1024
- npm create vite@latest my-react-app -- --template react
1025
- ```
1026
-
1027
- This command will create a new directory called `my-react-app` with a basic React project structure.
1028
-
1029
- 3. Navigate to your new project directory:
1030
-
1031
- ```
1032
- cd my-react-app
1033
- ```
1034
-
1035
- 4. Install the project dependencies using npm:
1036
-
1037
- ```
1038
- npm install
1039
- ```
1040
-
1041
- This command will read the `package.json` file in your project and install all the necessary dependencies.
1042
-
1043
- 5. Now, add Routerino to your project as a dependency:
1044
-
1045
- ```
1046
- npm install routerino
1047
- ```
1048
-
1049
- This command will install the latest version of Routerino and save it to your `package.json` file under the `dependencies` section.
1050
-
1051
- With these steps, you'll have a new React project set up with Vite as the build tool and Routerino installed as a dependency. You can now start building your application with React & Routerino.
1052
-
1053
- ### Full React Example
1054
-
1055
- This example includes the full React configuration. It might take the place of `src/main.jsx` or an `index.js` file.
1056
-
1057
- ```jsx
1058
- import React from "react";
1059
- import { createRoot } from "react-dom/client";
1060
- import Routerino from "routerino";
1061
-
1062
- export const routes = [
1063
- {
1064
- path: "/",
1065
- element: <p>Welcome to Home</p>,
1066
- title: "Home",
1067
- description: "Welcome to my website!",
1068
- },
1069
- {
1070
- path: "/about/",
1071
- element: <p>About us...</p>,
1072
- title: "About",
1073
- description: "Learn more about us.",
1074
- },
1075
- {
1076
- path: "/contact/",
1077
- element: (
1078
- <div>
1079
- <h1>Contact Us</h1>
1080
- <p>
1081
- Please <a href="mailto:user@example.com">send us an email</a> at
1082
- user@example.com
1083
- </p>
1084
- </div>
1085
- ),
1086
- title: "Contact",
1087
- description: "Get in touch with us.",
1088
- },
195
+ { path: "/", element: <HomePage />, title: "Home" },
196
+ ...blogPosts.map((post) => ({
197
+ path: `/blog/${post.slug}/`,
198
+ element: <BlogPost post={post} />,
199
+ title: post.title,
200
+ description: post.excerpt,
201
+ imageUrl: post.featuredImage,
202
+ })),
1089
203
  ];
1090
-
1091
- const App = () => (
1092
- <main>
1093
- <nav>
1094
- <a href="/">Home</a>
1095
- </nav>
1096
-
1097
- <Routerino
1098
- title="Example.com"
1099
- notFoundTitle="Sorry, but this page does not exist."
1100
- errorTitle="Yikes! Something went wrong."
1101
- routes={routes}
1102
- />
1103
-
1104
- <footer>
1105
- <p>
1106
- Learn more <a href="/about/">about us</a> or{" "}
1107
- <a href="/contact/">contact us</a> today.
1108
- </p>
1109
- </footer>
1110
- </main>
1111
- );
1112
-
1113
- createRoot(document.getElementById("root")).render(<App />);
1114
- ```
1115
-
1116
- ## ErrorBoundary Component
1117
-
1118
- Routerino exports an `ErrorBoundary` component that you can use in your own applications to catch and handle React component errors gracefully. Fun fact: error boundary components are one of the last cases that still require using a React Class! Since this library aims to include everything you need to build a multiple page React SPA, and enable users to be able to know which component had an issue without confusing it with a Routerino bug.
1119
-
1120
- ### Import
1121
-
1122
- ```jsx
1123
- import { ErrorBoundary } from "routerino";
1124
204
  ```
1125
205
 
1126
- ### Usage
206
+ Each generated route gets its own static HTML page with proper meta tags and is automatically included in `sitemap.xml`.
1127
207
 
1128
- ```jsx
1129
- <ErrorBoundary
1130
- fallback={<div>Something went wrong. Please try again.</div>}
1131
- errorTitleString="Error | My Application"
1132
- debug={window.location.hostname === "localhost"} // Auto-enable on localhost
1133
- >
1134
- <MyComponent />
1135
- </ErrorBoundary>
1136
- ```
208
+ ## Who Is This For?
1137
209
 
1138
- ### Props
210
+ Routerino is for React developers building content sites, marketing pages, or JAMstack apps who want full SEO (static HTML, meta tags, sitemaps) without adopting a framework like Next.js or Remix. If you're building a dashboard or an authenticated app, React Router is fine. If you need Google to index your pages, Routerino gives you that at build time with zero new dependencies.
1139
211
 
1140
- | Prop | Type | Required | Description |
1141
- | ------------------ | ----------- | -------- | ----------------------------------------------------------------- |
1142
- | `children` | `ReactNode` | No | The child components to render when there's no error |
1143
- | `fallback` | `ReactNode` | No | The UI to display when an error is caught |
1144
- | `errorTitleString` | `string` | Yes | The document title to set when an error occurs |
1145
- | `usePrerenderTags` | `boolean` | No | Whether to set prerender meta tags (status code 500) |
1146
- | `routePath` | `string` | No | The current route path for better error context (used internally) |
1147
- | `debug` | `boolean` | No | Enable detailed console logging of errors (default: `false`) |
212
+ ## TypeScript
1148
213
 
1149
- ### Debug Logging
1150
-
1151
- When `debug` is set to `true`, the ErrorBoundary provides detailed console output:
1152
-
1153
- - Groups error information for better readability
1154
- - Logs the component stack trace showing exactly where the error occurred
1155
- - Shows the failed route path (if available)
1156
- - Includes timestamp of when the error occurred
1157
-
1158
- **Recommended debug pattern:**
1159
-
1160
- ```jsx
1161
- // Auto-enable debug mode on localhost
1162
- debug={window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1"}
1163
-
1164
- // Or use environment variable
1165
- debug={process.env.NODE_ENV === "development"}
1166
- ```
214
+ TypeScript definitions are included. Routes are typed as `RouteConfig[]`:
1167
215
 
1168
- ### Features
216
+ ```tsx
217
+ import type { RouteConfig } from "routerino";
1169
218
 
1170
- - Catches JavaScript errors in child component tree
1171
- - Displays fallback UI instead of white screen
1172
- - Sets document title on error
1173
- - Provides detailed debug logging when enabled
1174
- - Optionally sets prerender status code for SEO
1175
- - Used internally by Routerino to protect route components
1176
-
1177
- This is the same error boundary used internally by Routerino to protect your route components from crashing the entire application.
1178
-
1179
- ## Vendoring Routerino
1180
-
1181
- If you prefer to include Routerino directly in your project instead of using it as a dependency, you can easily vendor the library. Vendoring allows you to have full control over the version of Routerino used in your project and eliminates the need to manage it as an external dependency.
1182
-
1183
- To vendor Routerino, follow these steps:
1184
-
1185
- 1. Download the `routerino.jsx` file from the Routerino [repository](./routerino.jsx).
1186
-
1187
- 2. Place the `routerino.jsx` file in a suitable location within your project's source directory. For example, you could create a `vendor` folder and place the file there:
1188
-
1189
- ```md
1190
- your-project/
1191
- ├── src/
1192
- │ ├── vendor/
1193
- │ │ └── routerino.jsx
1194
- │ └── ...
1195
- └── ...
1196
- ```
1197
-
1198
- 3. Update your import statements to reference the vendored `routerino.jsx` file instead of the npm package:
1199
-
1200
- ```jsx
1201
- // Before (importing from the package)
1202
- import Routerino from "routerino";
1203
-
1204
- // After (importing from the vendored file)
1205
- import Routerino from "./vendor/routerino";
1206
- ```
1207
-
1208
- 4. You're all set! Routerino is now vendored in your project, and you can use it as before.
1209
-
1210
- 5. If using the Routerino Forge plugin for SSG and to automatically generate a sitemap during the build process, be sure to copy it over as well.
1211
-
1212
- By vendoring Routerino, you have full control over the code and can make any necessary modifications directly to the `routerino.jsx` file. However, keep in mind that you'll need to manually update the vendored file if you want to incorporate any future updates or bug fixes from the main Routerino repository.
1213
-
1214
- ## Accessibility & SEO Best Practices
1215
-
1216
- To maximize your PageSpeed Insights and Lighthouse scores, we recommend setting up ESLint with accessibility rules. This helps catch common issues that hurt SEO and user experience.
1217
-
1218
- ### Setting up eslint-plugin-jsx-a11y
1219
-
1220
- 1. Install the plugin:
1221
-
1222
- ```bash
1223
- npm install --save-dev eslint-plugin-jsx-a11y
1224
- ```
1225
-
1226
- 2. Add to your ESLint config:
1227
-
1228
- ```javascript
1229
- // eslint.config.js (ESLint 9+ flat config)
1230
- import jsxA11y from "eslint-plugin-jsx-a11y";
1231
-
1232
- export default [
1233
- {
1234
- plugins: {
1235
- "jsx-a11y": jsxA11y,
1236
- },
1237
- rules: {
1238
- ...jsxA11y.configs.recommended.rules,
1239
- },
1240
- },
219
+ export const routes: RouteConfig[] = [
220
+ { path: "/", element: <HomePage />, title: "Home", description: "Welcome" },
1241
221
  ];
1242
222
  ```
1243
223
 
1244
- 3. Add a lint run script
1245
-
1246
- ```json
1247
- {
1248
- "scripts": {
1249
- "lint:": "eslint --ext .jsx,.js src/"
1250
- }
1251
- }
1252
- ```
1253
-
1254
- See https://github.com/jsx-eslint/eslint-plugin-jsx-a11y for more info.
1255
-
1256
- ### Key Rules for Accessibility
1257
-
1258
- 1. **Images**: Include descriptive `alt` text
1259
-
1260
- ```jsx
1261
- // Bad - Missing alt text
1262
- <img src="/logo.png" />
1263
-
1264
- // Good - Descriptive alt text
1265
- <img src="/logo.png" alt="Company logo" />
1266
-
1267
- // Good - Decorative images
1268
- <img src="/decoration.png" alt="" role="presentation" />
1269
- ```
1270
-
1271
- 2. **Heading Hierarchy**: Use proper heading order
1272
-
1273
- ```jsx
1274
- // Bad - Skipping heading levels
1275
- <h1>Page Title</h1>
1276
- <h3>Subsection</h3> // Should be h2
1277
-
1278
- // Good - Proper hierarchy
1279
- <h1>Page Title</h1>
1280
- <h2>Main Section</h2>
1281
- <h3>Subsection</h3>
1282
- ```
1283
-
1284
- 3. **Link Text**: Avoid generic link text
1285
-
1286
- ```jsx
1287
- // Bad - Generic text
1288
- <a href="/products">Click here</a>
1289
-
1290
- // ✅ Good - Descriptive text
1291
- <a href="/products">View our products</a>
1292
- ```
1293
-
1294
- 4. **ARIA Labels**: Use for icon-only buttons
1295
-
1296
- ```jsx
1297
- // Good - Icon button with label
1298
- <button aria-label="Close dialog">
1299
- <svg>...</svg>
1300
- </button>
1301
- ```
1302
-
1303
- These practices will help you achieve better accessibility scores and ensure your site is accessible to all users.
1304
-
1305
- ## Additional Resources
1306
-
1307
- Here are some sources for further reading on SEO best-practices.
1308
-
1309
- - [Optimize Largest Contentful Paint (LCP)](https://web.dev/articles/optimize-lcp) - Improve loading performance
1310
- - [Apple's best practices for link previews](https://developer.apple.com/library/archive/technotes/tn2444/_index.html)
1311
- - [Use Open Graph tags](https://ahrefs.com/blog/open-graph-meta-tags/)
1312
- - [Use descriptive link text](https://developers.google.com/search/docs/fundamentals/seo-starter-guide?hl=en&ref_topic=9460495)
1313
-
1314
- ## Contributions
1315
-
1316
- Contributions are always welcome. Please feel free to create an issue or submit a pull request. Just remember to keep it simple!
224
+ ## Documentation
225
+
226
+ - [Getting Started](docs/getting-started.md) — Full React example, Preact setup
227
+ - [SEO Guide](docs/seo-guide.md) — Canonical URLs, social previews, JSON-LD, hash links
228
+ - [Image Optimization](docs/image-optimization.md) — Delegate to `vite-plugin-image-optimizer`
229
+ - [Accessibility](docs/accessibility.md) ESLint a11y setup for Lighthouse scores
230
+ - [Vendoring](docs/vendoring.md) — Include Routerino directly in your project
231
+ - [Additional Resources](docs/additional-resources.md) — External SEO/performance links
232
+
233
+ ## API Reference
234
+
235
+ ### `<Routerino>` Props
236
+
237
+ | Prop | Type | Default | Description |
238
+ | ------------------ | --------------- | ------------------------ | ----------------------------------------------- |
239
+ | `title` | `string` | `""` | Site title appended to page titles |
240
+ | `routes` | `RouteConfig[]` | `[default]` | Array of route configurations |
241
+ | `separator` | `string` | `" \| "` | Title separator between page and site title |
242
+ | `notFoundTemplate` | `ReactNode` | Built-in 404 | 404 page template |
243
+ | `notFoundTitle` | `string` | `"Page not found [404]"` | 404 page title |
244
+ | `errorTemplate` | `ReactNode` | Built-in 500 | Error page template |
245
+ | `errorTitle` | `string` | `"Page error [500]"` | Error page title |
246
+ | `useTrailingSlash` | `boolean` | `true` | Use trailing slashes in canonical URLs |
247
+ | `usePrerenderTags` | `boolean` | `false` | Include prerender meta tags for crawlers |
248
+ | `baseUrl` | `string` | `null` | Base URL for canonical tags (no trailing slash) |
249
+ | `imageUrl` | `string` | `null` | Default site-wide social image URL |
250
+ | `touchIconUrl` | `string` | `null` | PWA homescreen icon URL |
251
+ | `debug` | `boolean` | `false` | Enable console logging |
252
+ | `ignorePatterns` | `string[]` | `[]` | URL patterns to skip SPA routing |
253
+
254
+ ### `RouteConfig` Object
255
+
256
+ | Field | Type | Required | Description |
257
+ | ------------- | ----------- | -------- | --------------------------------------------------- |
258
+ | `path` | `string` | Yes | Route path. Must start with `/`. Supports `:param`. |
259
+ | `element` | `ReactNode` | Yes | Component to render at this route |
260
+ | `title` | `string` | No | Page title (site title appended automatically) |
261
+ | `description` | `string` | No | Meta description |
262
+ | `imageUrl` | `string` | No | Social preview image for this route |
263
+ | `tags` | `HeadTag[]` | No | Additional head tags (OG, JSON-LD, etc.) |
264
+
265
+ ### `HeadTag` Object
266
+
267
+ Common attributes: `tag`, `name`, `property`, `content`, `rel`, `href`, `soft`, `innerHTML`. Supports all standard HTML attributes. See [updateHeadTag](#updateheadtag) for details.
268
+
269
+ ### `routerinoForge` Options
270
+
271
+ | Option | Type | Default | Description |
272
+ | ------------------ | --------- | --------------------------------------- | ------------------------------------------------ |
273
+ | `baseUrl` | `string` | **required** | Production URL (no trailing slash) |
274
+ | `routes` | `string` | `"./src/routes.jsx"` | Path to routes file |
275
+ | `outputDir` | `string` | `"dist"` | Build output directory |
276
+ | `generateSitemap` | `boolean` | `true` | Generate sitemap.xml and robots.txt |
277
+ | `useTrailingSlash` | `boolean` | `true` | Set to `false` for `/about` instead of `/about/` |
278
+ | `verbose` | `boolean` | `false` | Enable detailed build logging |
279
+ | `ssgCacheDir` | `string` | `"node_modules/.cache/routerino-forge"` | SSG cache directory |
280
+
281
+ ## Contributing
282
+
283
+ Contributions are welcome. Please create an issue or submit a pull request. Keep it simple!
1317
284
 
1318
285
  ## License
1319
286