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/docs/README.md ADDED
@@ -0,0 +1,8 @@
1
+ # Routerino Documentation
2
+
3
+ - [Getting Started](./getting-started.md) — New project setup, full React example
4
+ - [SEO Guide](./seo-guide.md) — Page titles, canonical URLs, social previews, meta descriptions, hash links, structured data
5
+ - [Image Optimization](./image-optimization.md) — Delegating image optimization to `vite-plugin-image-optimizer`
6
+ - [Accessibility](./accessibility.md) — ESLint a11y setup, best practices for Lighthouse scores
7
+ - [Vendoring](./vendoring.md) — Including Routerino directly in your project
8
+ - [Additional Resources](./additional-resources.md) — External links for further reading
@@ -0,0 +1,87 @@
1
+ # Accessibility & Lighthouse Scores
2
+
3
+ To maximize your PageSpeed Insights and Lighthouse scores, set up ESLint with accessibility rules.
4
+
5
+ ## Setting up eslint-plugin-jsx-a11y
6
+
7
+ 1. Install the plugin:
8
+
9
+ ```sh
10
+ npm install --save-dev eslint-plugin-jsx-a11y
11
+ ```
12
+
13
+ 2. Add to your ESLint config:
14
+
15
+ ```js
16
+ // eslint.config.js (ESLint 9+ flat config)
17
+ import jsxA11y from "eslint-plugin-jsx-a11y";
18
+
19
+ export default [
20
+ {
21
+ plugins: {
22
+ "jsx-a11y": jsxA11y,
23
+ },
24
+ rules: {
25
+ ...jsxA11y.configs.recommended.rules,
26
+ },
27
+ },
28
+ ];
29
+ ```
30
+
31
+ 3. Add a lint run script:
32
+
33
+ ```json
34
+ {
35
+ "scripts": {
36
+ "lint": "eslint --ext .jsx,.js src/"
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## Key Rules
42
+
43
+ ### Images: Include descriptive `alt` text
44
+
45
+ ```jsx
46
+ // Bad - Missing alt text
47
+ <img src="/logo.png" />
48
+
49
+ // Good - Descriptive alt text
50
+ <img src="/logo.png" alt="Company logo" />
51
+
52
+ // Good - Decorative images
53
+ <img src="/decoration.png" alt="" role="presentation" />
54
+ ```
55
+
56
+ ### Heading Hierarchy: Use proper heading order
57
+
58
+ ```jsx
59
+ // Bad - Skipping heading levels
60
+ <h1>Page Title</h1>
61
+ <h3>Subsection</h3> // Should be h2
62
+
63
+ // Good - Proper hierarchy
64
+ <h1>Page Title</h1>
65
+ <h2>Main Section</h2>
66
+ <h3>Subsection</h3>
67
+ ```
68
+
69
+ ### Link Text: Avoid generic link text
70
+
71
+ ```jsx
72
+ // Bad - Generic text
73
+ <a href="/products">Click here</a>
74
+
75
+ // Good - Descriptive text
76
+ <a href="/products">View our products</a>
77
+ ```
78
+
79
+ ### ARIA Labels: Use for icon-only buttons
80
+
81
+ ```jsx
82
+ <button aria-label="Close dialog">
83
+ <svg>...</svg>
84
+ </button>
85
+ ```
86
+
87
+ See [eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) for more info.
@@ -0,0 +1,6 @@
1
+ # Additional Resources
2
+
3
+ - [Optimize Largest Contentful Paint (LCP)](https://web.dev/articles/optimize-lcp) — Improve loading performance
4
+ - [Apple's best practices for link previews](https://developer.apple.com/library/archive/technotes/tn2444/_index.html)
5
+ - [Use Open Graph tags](https://ahrefs.com/blog/open-graph-meta-tags/)
6
+ - [Use descriptive link text](https://developers.google.com/search/docs/fundamentals/seo-starter-guide)
@@ -0,0 +1,134 @@
1
+ # Getting Started with Routerino
2
+
3
+ ## Starting a New React Project
4
+
5
+ If you're starting from scratch, here's the recommended approach:
6
+
7
+ 1. Ensure you have [Node.js](https://nodejs.org/) and npm installed. Consider using a Node version manager like [Volta](https://volta.sh/), [fnm](https://github.com/Schniz/fnm), or [asdf](https://asdf-vm.com/).
8
+
9
+ 2. Create a new React project with [Vite](https://vitejs.dev/):
10
+
11
+ ```sh
12
+ npm create vite@latest my-react-app -- --template react
13
+ ```
14
+
15
+ 3. Install dependencies:
16
+
17
+ ```sh
18
+ cd my-react-app
19
+ npm install
20
+ ```
21
+
22
+ 4. Add Routerino:
23
+
24
+ ```sh
25
+ npm install routerino
26
+ ```
27
+
28
+ ## Full React Example
29
+
30
+ This example includes the full React configuration. It can replace `src/main.jsx`:
31
+
32
+ ```jsx
33
+ import React from "react";
34
+ import { createRoot } from "react-dom/client";
35
+ import Routerino from "routerino";
36
+
37
+ export const routes = [
38
+ {
39
+ path: "/",
40
+ element: <p>Welcome to Home</p>,
41
+ title: "Home",
42
+ description: "Welcome to my website!",
43
+ },
44
+ {
45
+ path: "/about/",
46
+ element: <p>About us...</p>,
47
+ title: "About",
48
+ description: "Learn more about us.",
49
+ },
50
+ {
51
+ path: "/contact/",
52
+ element: (
53
+ <div>
54
+ <h1>Contact Us</h1>
55
+ <p>
56
+ Please <a href="mailto:user@example.com">send us an email</a> at
57
+ user@example.com
58
+ </p>
59
+ </div>
60
+ ),
61
+ title: "Contact",
62
+ description: "Get in touch with us.",
63
+ },
64
+ ];
65
+
66
+ const App = () => (
67
+ <main>
68
+ <nav>
69
+ <a href="/">Home</a>
70
+ </nav>
71
+
72
+ <Routerino
73
+ title="Example.com"
74
+ notFoundTitle="Sorry, but this page does not exist."
75
+ errorTitle="Yikes! Something went wrong."
76
+ routes={routes}
77
+ />
78
+
79
+ <footer>
80
+ <p>
81
+ Learn more <a href="/about/">about us</a> or{" "}
82
+ <a href="/contact/">contact us</a> today.
83
+ </p>
84
+ </footer>
85
+ </main>
86
+ );
87
+
88
+ createRoot(document.getElementById("root")).render(<App />);
89
+ ```
90
+
91
+ ## Using Preact
92
+
93
+ Routerino is fully compatible with Preact via `@preact/compat`:
94
+
95
+ 1. Install Preact:
96
+
97
+ ```sh
98
+ npm i preact @preact/compat
99
+ ```
100
+
101
+ 2. Configure your bundler:
102
+
103
+ **Vite:**
104
+
105
+ ```js
106
+ import { defineConfig } from "vite";
107
+ import preact from "@preact/preset-vite";
108
+
109
+ export default defineConfig({
110
+ plugins: [preact()],
111
+ resolve: {
112
+ alias: {
113
+ react: "@preact/compat",
114
+ "react-dom": "@preact/compat",
115
+ "react/jsx-runtime": "@preact/compat/jsx-runtime",
116
+ },
117
+ },
118
+ });
119
+ ```
120
+
121
+ **Webpack:**
122
+
123
+ ```js
124
+ module.exports = {
125
+ resolve: {
126
+ alias: {
127
+ react: "preact/compat",
128
+ "react-dom": "preact/compat",
129
+ },
130
+ },
131
+ };
132
+ ```
133
+
134
+ 3. Use Routerino exactly as you would in a React project — the API is identical.
@@ -0,0 +1,30 @@
1
+ # Image Optimization
2
+
3
+ Routerino delegates image optimization to [`vite-plugin-image-optimizer`](https://github.com/FatehAK/vite-plugin-image-optimizer). Install it along with `sharp` and add it to your Vite config **before** `routerinoForge`:
4
+
5
+ ```sh
6
+ npm install --save-dev vite-plugin-image-optimizer sharp
7
+ ```
8
+
9
+ ```js
10
+ // vite.config.js
11
+ import { defineConfig } from "vite";
12
+ import react from "@vitejs/plugin-react";
13
+ import { ViteImageOptimizer } from "vite-plugin-image-optimizer";
14
+ import { routerinoForge } from "routerino/forge";
15
+
16
+ export default defineConfig({
17
+ plugins: [
18
+ react(),
19
+ ViteImageOptimizer({
20
+ jpg: { quality: 80 },
21
+ jpeg: { quality: 80 },
22
+ png: { quality: 80 },
23
+ webp: { quality: 80 },
24
+ }),
25
+ routerinoForge({ baseUrl: "https://example.com" }),
26
+ ],
27
+ });
28
+ ```
29
+
30
+ 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.
@@ -0,0 +1,137 @@
1
+ # SEO Guide
2
+
3
+ ## Page Titles
4
+
5
+ - Keep page titles unique for each route. Avoid including the site name like "Foo.com" in individual page titles — Routerino adds that automatically.
6
+ - Aim for concise, descriptive titles that accurately represent the page content.
7
+ - Keep title length at a max of 50-60 characters. Longer text may be ignored or cut off, especially on mobile.
8
+
9
+ ## URL Structure & Canonicalization
10
+
11
+ 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. Routerino handles this automatically.
12
+
13
+ **With SSG:** Creates both `/about.html` and `/about/index.html` with canonical tags:
14
+
15
+ ```html
16
+ <link rel="canonical" href="https://example.com/about/" />
17
+ <meta property="og:url" content="https://example.com/about/" />
18
+ ```
19
+
20
+ **With Prerender (SPA):** Includes meta tags for correct status codes:
21
+
22
+ ```html
23
+ <!-- For canonical URL (/about/) -->
24
+ <link rel="canonical" href="https://example.com/about/" />
25
+ <meta property="og:url" content="https://example.com/about/" />
26
+
27
+ <!-- For non-canonical URL (/about) -->
28
+ <meta name="prerender-status-code" content="301" />
29
+ <meta name="prerender-header" content="Location: https://example.com/about/" />
30
+ ```
31
+
32
+ Use `useTrailingSlash={false}` if you prefer URLs without trailing slashes. Use `baseUrl` if your site is served from multiple domains:
33
+
34
+ ```jsx
35
+ <Routerino baseUrl="https://example.com" routes={routes} />
36
+ ```
37
+
38
+ ## Social Previews & Open Graph
39
+
40
+ Routerino automatically sets core Open Graph tags (`og:title`, `og:description`, `og:url`, `og:image`) for every page.
41
+
42
+ ### Image Best Practices
43
+
44
+ - Size: Use 1200x630 pixels (1.91:1 ratio) for maximum compatibility
45
+ - Add dimensions for faster first-share rendering:
46
+
47
+ ```js
48
+ updateHeadTag({ property: "og:image:width", content: "1200" });
49
+ updateHeadTag({ property: "og:image:height", content: "630" });
50
+ ```
51
+
52
+ ### Branding
53
+
54
+ For site-wide branding, add `og:site_name`:
55
+
56
+ ```js
57
+ updateHeadTag({ property: "og:site_name", content: "Your Brand" });
58
+ ```
59
+
60
+ ### Platform-Specific Enhancements
61
+
62
+ - Apple/iMessage: Set `touchIconUrl` prop for iMessage link previews
63
+ - Video content:
64
+
65
+ ```js
66
+ updateHeadTag({
67
+ property: "og:video",
68
+ content: "https://example.com/video.mp4",
69
+ });
70
+ updateHeadTag({ property: "og:video:type", content: "video/mp4" });
71
+ ```
72
+
73
+ ### Testing Previews
74
+
75
+ Test how your links appear with platform-specific tools (platforms cache aggressively — use these to force a refresh):
76
+
77
+ - [Facebook Sharing Debugger](https://developers.facebook.com/tools/debug/)
78
+ - [LinkedIn Post Inspector](https://www.linkedin.com/post-inspector/)
79
+ - [Twitter Card Validator](https://cards-dev.twitter.com/validator)
80
+
81
+ ### Twitter Cards
82
+
83
+ Routerino automatically includes `summary_large_image` for maximum engagement:
84
+
85
+ ```html
86
+ <meta name="twitter:card" content="summary_large_image" />
87
+ ```
88
+
89
+ ## Meta Descriptions
90
+
91
+ - Provide unique, informative descriptions for each route.
92
+ - Keep them under ~150 characters to avoid truncation in search results.
93
+
94
+ ## Structured Data (JSON-LD)
95
+
96
+ Use the `innerHTML` property on a `<script type="application/ld+json">` head tag:
97
+
98
+ ```jsx
99
+ {
100
+ path: "/about/",
101
+ element: <AboutPage />,
102
+ tags: [{
103
+ tag: "script",
104
+ type: "application/ld+json",
105
+ innerHTML: JSON.stringify({
106
+ "@context": "https://schema.org",
107
+ "@type": "BreadcrumbList",
108
+ itemListElement: [
109
+ { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com/" },
110
+ { "@type": "ListItem", "position": 2, "name": "About", "item": "https://example.com/about/" },
111
+ ],
112
+ }),
113
+ }],
114
+ }
115
+ ```
116
+
117
+ This works both at runtime (via `updateHeadTag`) and during SSG (via the forge plugin).
118
+
119
+ ## Hash Links
120
+
121
+ 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.
122
+
123
+ **Sticky headers:** Use [`scroll-margin-top`](https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-margin-top) to offset the scroll target:
124
+
125
+ ```css
126
+ [id] {
127
+ scroll-margin-top: 80px; /* match your header height */
128
+ }
129
+ ```
130
+
131
+ This works for SPA navigation, native browser hash navigation, and direct page loads.
132
+
133
+ ## Additional SEO Considerations
134
+
135
+ - Use semantic HTML elements in your components for better content structure.
136
+ - Implement structured data (JSON-LD) where applicable to enhance rich snippets.
137
+ - Ensure your site is mobile-friendly and loads quickly.
@@ -0,0 +1,30 @@
1
+ # Vendoring Routerino
2
+
3
+ If you prefer to include Routerino directly in your project instead of using it as a dependency, you can vendor the library. This gives you full control over the version and eliminates managing it as an external dependency.
4
+
5
+ 1. Download `routerino.jsx` from the [repository](../routerino.jsx).
6
+
7
+ 2. Place it in a suitable location within your project:
8
+
9
+ ```
10
+ your-project/
11
+ ├── src/
12
+ │ ├── vendor/
13
+ │ │ └── routerino.jsx
14
+ │ └── ...
15
+ └── ...
16
+ ```
17
+
18
+ 3. Update your imports:
19
+
20
+ ```jsx
21
+ // Before (importing from the package)
22
+ import Routerino from "routerino";
23
+
24
+ // After (importing from the vendored file)
25
+ import Routerino from "./vendor/routerino";
26
+ ```
27
+
28
+ 4. If using the Routerino Forge plugin for SSG and sitemap generation, copy `routerino-forge.js` as well.
29
+
30
+ **Note:** When vendoring, you'll need to manually update the vendored files to incorporate future updates or bug fixes from the main repository.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "routerino",
3
- "version": "2.6.0",
4
- "description": "A lightweight, SEO-optimized React router for modern web applications",
3
+ "version": "2.6.2",
4
+ "description": "The React router that Google can read. Built-in SSG, meta tags, sitemaps, and canonical URLs — zero dependencies, no framework lock-in.",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/nerds-with-keyboards/routerino.git"
@@ -11,7 +11,17 @@
11
11
  "react",
12
12
  "seo",
13
13
  "prerender",
14
- "react router"
14
+ "ssg",
15
+ "static-site",
16
+ "static-site-generator",
17
+ "vite",
18
+ "vite-plugin",
19
+ "meta-tags",
20
+ "open-graph",
21
+ "sitemap",
22
+ "seo-friendly",
23
+ "jamstack",
24
+ "zero-dependency"
15
25
  ],
16
26
  "license": "MIT",
17
27
  "bugs": {
@@ -27,7 +27,6 @@ export function routerinoForge(options = {}) {
27
27
  useTrailingSlash: options.useTrailingSlash ?? true, // Default to trailing slashes
28
28
  ssgCacheDir:
29
29
  options.ssgCacheDir || "node_modules/.cache/routerino-forge/ssg",
30
- nonBlockingCss: options.nonBlockingCss ?? true,
31
30
  };
32
31
 
33
32
  // Normalize baseUrl: strip trailing slashes to ensure correct canonical composition
@@ -54,15 +53,6 @@ export function routerinoForge(options = {}) {
54
53
  viteConfig = resolvedConfig;
55
54
  },
56
55
 
57
- transformIndexHtml(html) {
58
- if (!config.nonBlockingCss) return html;
59
- return html.replace(
60
- /<link rel="stylesheet"[^>]*href="(\/[^"]+\.css)"[^>]*\/?>/g,
61
- (_, href) =>
62
- `<link rel="preload" as="style" href="${href}">\n <link rel="stylesheet" href="${href}" media="print" onload="this.media='all'">\n <noscript><link rel="stylesheet" href="${href}"></noscript>`
63
- );
64
- },
65
-
66
56
  async closeBundle() {
67
57
  // Only run during build, not during dev server
68
58
  if (viteConfig.command !== "build") return;
@@ -41,13 +41,6 @@ export interface HeadTag {
41
41
  * @default "node_modules/.cache/routerino-forge/ssg"
42
42
  */
43
43
  ssgCacheDir?: string;
44
- /**
45
- * Transform CSS `<link>` tags to load asynchronously (non-blocking).
46
- * Converts stylesheets to use `media="print"` with `onload` swap,
47
- * `<link rel="preload">` hint, and `<noscript>` fallback.
48
- * @default true
49
- */
50
- nonBlockingCss?: boolean;
51
44
  }
52
45
 
53
46
  export interface HeadTag {