reroute-js 0.0.13 → 0.1.0

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 (37) hide show
  1. package/README.md +17 -8
  2. package/_/blog/src/client/components/Counter.tsx +14 -0
  3. package/_/blog/src/client/components/RecentPosts.tsx +90 -0
  4. package/_/blog/src/client/index.html +10 -9
  5. package/_/blog/src/client/index.tsx +1 -1
  6. package/_/blog/src/client/routes/[404].tsx +15 -12
  7. package/_/blog/src/client/routes/about.tsx +10 -4
  8. package/_/blog/src/client/routes/blog/[404].tsx +15 -5
  9. package/_/blog/src/client/routes/blog/[layout].tsx +1 -1
  10. package/_/blog/src/client/routes/blog/content/1-hello-world.tsx +27 -0
  11. package/_/blog/src/client/routes/blog/content/2-what-is-reroute.tsx +31 -0
  12. package/_/blog/src/client/routes/blog/index.tsx +3 -2
  13. package/_/blog/src/client/routes/index.tsx +2 -14
  14. package/_/blog/src/index.ts +2 -2
  15. package/_/store/package.json +25 -0
  16. package/_/store/src/client/App.tsx +17 -0
  17. package/_/store/src/client/components/Header.tsx +40 -0
  18. package/_/store/src/client/components/ProductCard.tsx +51 -0
  19. package/_/store/src/client/index.html +17 -0
  20. package/_/store/src/client/index.tsx +7 -0
  21. package/_/store/src/client/lib/api.ts +153 -0
  22. package/_/store/src/client/routes/[404].tsx +63 -0
  23. package/_/store/src/client/routes/categories/[category].tsx +223 -0
  24. package/_/store/src/client/routes/categories/index.tsx +187 -0
  25. package/_/store/src/client/routes/index.tsx +126 -0
  26. package/_/store/src/client/routes/products/[id].tsx +233 -0
  27. package/_/store/src/client/routes/products/index.tsx +261 -0
  28. package/_/store/src/client/theme.css +306 -0
  29. package/_/store/src/index.ts +19 -0
  30. package/_/store/tsconfig.json +26 -0
  31. package/cli/bin.js +45 -16
  32. package/cli/bin.js.map +4 -4
  33. package/cli/src/cli.d.ts.map +1 -1
  34. package/package.json +2 -2
  35. package/_/blog/src/client/components/.gitkeep +0 -0
  36. package/_/blog/src/client/routes/blog/content/getting-started.tsx +0 -30
  37. package/_/blog/src/client/routes/blog/content/hello-world.tsx +0 -29
package/README.md CHANGED
@@ -12,7 +12,7 @@ A dead simple file-based routing framework for building fullstack React apps on
12
12
  - 📦 **Zero Config** - Works out of the box with sensible defaults
13
13
 
14
14
  ### 🛠️ CLI Tools
15
- - 🚀 **`reroute init`** - Scaffold new projects with templates (basic, blog)
15
+ - 🚀 **`reroute init`** - Scaffold new projects with templates (basic, blog, store)
16
16
  - 🔨 **`reroute gen`** - Generate content registry and route artifacts
17
17
 
18
18
  ### 📄 Content Management
@@ -24,6 +24,7 @@ A dead simple file-based routing framework for building fullstack React apps on
24
24
  - 📦 **Collection Chunking** - Individual module bundles per content item
25
25
 
26
26
  ### ⚛️ React Integration
27
+ - ⚡ **React 19** - Built with the latest React version for optimal performance
27
28
  - 🪝 **Router Hooks**:
28
29
  - `useNavigate()` - Programmatic navigation
29
30
  - `useParams()` - Access route parameters
@@ -39,9 +40,9 @@ A dead simple file-based routing framework for building fullstack React apps on
39
40
  - `<RouterProvider>` - Router context for the app
40
41
  - `<ContentProvider>` - Content system context
41
42
  - `<RerouteProvider>` - All-in-one provider wrapper
42
- - 🗂️ **SSR Data**:
43
- - `useData()` - Read route-level SSR data without loaders
44
- - `export const ssr = { data() {} }` - Route data function executed on server
43
+ - 🗂️ **SSR Data**:
44
+ - `useData()` - Read route-level SSR data without loaders
45
+ - `export const ssr = { data() {} }` - Route data function executed on server
45
46
 
46
47
  ### 🚀 Performance Optimizations
47
48
  - 💾 **Smart Caching** - LRU cache for files and bundles
@@ -51,8 +52,15 @@ A dead simple file-based routing framework for building fullstack React apps on
51
52
  - 🎯 **SSR Module Seeding** - Pre-populate modules for instant hydration
52
53
  - 📊 **Collection Inlining** - Inline collection data for zero-latency rendering
53
54
 
55
+ ### 🎨 Styling & Theming
56
+ - 🎨 **Tailwind CSS v4 Support** - Automatic detection and compilation with CSS-first configuration
57
+ - ⚡ **Live CSS Reload** - Instant style updates in development mode
58
+ - 🎯 **Modern CSS Features** - `@theme` and `@utility` directives support
59
+ - 🚀 **Oxide Engine** - 5x faster builds with the new Tailwind compiler
60
+ - 🔧 **Zero Config** - Works out of the box when `@tailwindcss/cli` is installed
61
+
54
62
  ### 🎨 Developer Experience
55
- - 📁 **Project Templates** - Quick start with `basic` or `blog` templates
63
+ - 📁 **Project Templates** - Quick start with `basic`, `blog`, or `store` templates
56
64
  - 🔄 **Live Reload** - Server-Sent Events (SSE) for instant browser updates
57
65
  - 🎯 **TypeScript Support** - Full type safety throughout the stack
58
66
  - 🗂️ **File System Watcher** - Automatic rebuilds on source changes
@@ -85,17 +93,18 @@ A dead simple file-based routing framework for building fullstack React apps on
85
93
  - 🔧 **URL Prefix** - Deploy to subdirectories with custom prefixes
86
94
  - 🚫 **Ignore Patterns** - Exclude files from static serving
87
95
  - 🏷️ **Custom Headers** - Add headers to static file responses
88
- - ⏰ **Cache Control** - Configurable max-age and cache directives for static files and `__reroute_data` JSON
96
+ - ⏰ **Cache Control** - Configurable max-age and cache directives for static files and `__reroute_data` JSON
89
97
  - 🎨 **HTML Template** - Custom index.html with variable substitution
90
98
 
91
99
  ## 🚀 Quick Start
92
100
 
93
101
  ```bash
94
- # Create a new project
102
+ # Create a new project (basic template)
95
103
  bunx reroute-js init my-app
96
104
 
97
- # Or with a template
105
+ # Or with a specific template
98
106
  bunx reroute-js init my-blog --template blog
107
+ bunx reroute-js init my-store --template store
99
108
 
100
109
  # Navigate to project
101
110
  cd my-app
@@ -0,0 +1,14 @@
1
+ import { useState } from 'react';
2
+
3
+ export default function Counter() {
4
+ const [count, setCount] = useState(0);
5
+ const increase = () => setCount((c) => c + 1);
6
+
7
+ return (
8
+ <section>
9
+ <h2>Current count: {count}</h2>
10
+ {/* biome-ignore lint/a11y/useButtonType: shut up */}
11
+ <button onClick={increase}>Increase</button>
12
+ </section>
13
+ );
14
+ }
@@ -0,0 +1,90 @@
1
+ import { Link, useContent } from 'reroute-js/react';
2
+
3
+ interface RecentPostsProps {
4
+ limit?: number;
5
+ }
6
+
7
+ function RecentPosts({ limit = 3 }: RecentPostsProps) {
8
+ const { items } = useContent({
9
+ collection: 'blog',
10
+ limit,
11
+ });
12
+
13
+ const recentPosts = items;
14
+
15
+ return (
16
+ <div style={{ marginTop: '2rem' }}>
17
+ <h2>Recent Blog Posts</h2>
18
+ <div
19
+ style={{
20
+ marginTop: '1rem',
21
+ display: 'flex',
22
+ flexDirection: 'column',
23
+ gap: '1rem',
24
+ }}
25
+ >
26
+ {recentPosts.map((post) => (
27
+ <article
28
+ key={post.slug}
29
+ style={{
30
+ padding: '1rem',
31
+ border: '1px solid #e0e0e0',
32
+ borderRadius: '8px',
33
+ background: '#fff',
34
+ }}
35
+ >
36
+ <h3 style={{ margin: '0 0 0.5rem 0', fontSize: '1.25rem' }}>
37
+ <Link
38
+ to={post.href}
39
+ style={{ textDecoration: 'none', color: '#0066cc' }}
40
+ >
41
+ {String(post.meta?.title || post.name)}
42
+ </Link>
43
+ </h3>
44
+ {post.meta?.date && (
45
+ <time style={{ fontSize: '0.875rem', color: '#666' }}>
46
+ {String(post.meta.date)}
47
+ </time>
48
+ )}
49
+ {post.meta?.excerpt && (
50
+ <p
51
+ style={{
52
+ marginTop: '0.5rem',
53
+ color: '#333',
54
+ fontSize: '0.9rem',
55
+ }}
56
+ >
57
+ {String(post.meta.excerpt)}
58
+ </p>
59
+ )}
60
+ <Link
61
+ to={post.href}
62
+ style={{
63
+ display: 'inline-block',
64
+ marginTop: '0.5rem',
65
+ color: '#0066cc',
66
+ textDecoration: 'none',
67
+ fontSize: '0.9rem',
68
+ }}
69
+ >
70
+ Read more →
71
+ </Link>
72
+ </article>
73
+ ))}
74
+ </div>
75
+ <div style={{ marginTop: '1rem' }}>
76
+ <Link
77
+ to='/blog'
78
+ style={{
79
+ color: '#0066cc',
80
+ textDecoration: 'none',
81
+ }}
82
+ >
83
+ View all posts →
84
+ </Link>
85
+ </div>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ export default RecentPosts;
@@ -1,12 +1,13 @@
1
1
  <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <title>{{PROJECT_NAME}}</title>
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="./index.tsx"></script>
11
- </body>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>{{PROJECT_NAME}}</title>
6
+
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ </head>
9
+ <body>
10
+ <div id="root"></div>
11
+ <script type="module" src="./index.tsx"></script>
12
+ </body>
12
13
  </html>
@@ -1,5 +1,5 @@
1
1
  import { hydrateRoot } from 'react-dom/client';
2
2
  import App from './App';
3
3
 
4
- // biome-ignore lint/style/noNonNullAssertion: root element required
4
+ // biome-ignore lint/style/noNonNullAssertion: all good
5
5
  hydrateRoot(document.getElementById('root')!, <App />);
@@ -1,18 +1,21 @@
1
- export default function NotFound() {
1
+ const meta = {
2
+ title: '404',
3
+ description: 'Page not found',
4
+ };
5
+
6
+ function NotFound() {
2
7
  return (
3
- <div
4
- style={{
5
- padding: '2rem',
6
- maxWidth: '800px',
7
- margin: '0 auto',
8
- textAlign: 'center',
9
- }}
10
- >
11
- <h1>404 - Page Not Found</h1>
12
- <p>The page you're looking for doesn't exist.</p>
8
+ <div style={{ padding: '2rem', maxWidth: '800px', margin: '0 auto' }}>
9
+ <h1>404</h1>
10
+ <p>The page you are looking for does not exist.</p>
13
11
  <nav style={{ marginTop: '2rem' }}>
14
- <a href='/'>← Back to Home</a>
12
+ <a href='/' style={{ marginRight: '1rem' }}>
13
+ ← Back to Home
14
+ </a>
15
15
  </nav>
16
16
  </div>
17
17
  );
18
18
  }
19
+
20
+ export default NotFound;
21
+ export { meta };
@@ -1,17 +1,23 @@
1
+ import RecentPosts from '../components/RecentPosts';
2
+
1
3
  const meta = {
2
- title: 'About',
3
- description: 'Learn more about our application',
4
+ title: 'About Us',
5
+ description:
6
+ 'Learn more about our application built with custom file-based routing!',
4
7
  };
5
8
 
6
9
  function About() {
7
10
  return (
8
11
  <div style={{ padding: '2rem', maxWidth: '800px', margin: '0 auto' }}>
9
- <h1>About</h1>
10
- <p>Welcome to your Reroute application!</p>
12
+ <h1>About Us</h1>
13
+ <p>Welcome to our application built with custom file-based routing!</p>
11
14
  <p>
12
15
  This page demonstrates a simple static route that maps from{' '}
13
16
  <code>routes/about.tsx</code> to <code>/about</code>.
14
17
  </p>
18
+
19
+ <RecentPosts limit={1} />
20
+
15
21
  <nav style={{ marginTop: '2rem' }}>
16
22
  <a href='/' style={{ marginRight: '1rem' }}>
17
23
  ← Back to Home
@@ -1,11 +1,21 @@
1
- export default function BlogNotFound() {
1
+ const meta = {
2
+ title: '404',
3
+ description: 'Page not found',
4
+ };
5
+
6
+ function NotFound() {
2
7
  return (
3
- <div style={{ padding: '2rem', textAlign: 'center' }}>
4
- <h1>Post Not Found</h1>
5
- <p>The blog post you're looking for doesn't exist.</p>
8
+ <div style={{ padding: '2rem', maxWidth: '800px', margin: '0 auto' }}>
9
+ <h1>404</h1>
10
+ <p>The blog content you are looking for does not exist.</p>
6
11
  <nav style={{ marginTop: '2rem' }}>
7
- <a href='/blog'>← Back to Blog</a>
12
+ <a href='/' style={{ marginRight: '1rem' }}>
13
+ ← Back to Home
14
+ </a>
8
15
  </nav>
9
16
  </div>
10
17
  );
11
18
  }
19
+
20
+ export default NotFound;
21
+ export { meta };
@@ -77,7 +77,7 @@ export default function BlogLayout() {
77
77
  background: '#fff',
78
78
  }}
79
79
  >
80
- <p>© 2024 {{ PROJECT_NAME }}. Built with Reroute.</p>
80
+ <p>© 2024 Reroute Blog. Built with Bun, Elysia, and React.</p>
81
81
  </footer>
82
82
  </div>
83
83
  );
@@ -0,0 +1,27 @@
1
+ // Page metadata and optional SSR extras. Reroute SSR will use these
2
+ // to generate <title>, <meta name="description"> and append any custom head.
3
+ const meta = {
4
+ title: 'Hello World',
5
+ description: 'This is the first post using Reroute 🎉',
6
+ excerpt: 'This is a great post',
7
+ date: '2025-10-20',
8
+ };
9
+
10
+ const ssr = {
11
+ head: [
12
+ '<meta property="og:type" content="article" />',
13
+ '<meta name="twitter:card" content="summary_large_image" />',
14
+ ],
15
+ };
16
+
17
+ function BlogPost() {
18
+ return (
19
+ <div>
20
+ <h1>Hello World</h1>
21
+ <p>Welcome to my blog!!</p>
22
+ </div>
23
+ );
24
+ }
25
+
26
+ export { meta, ssr };
27
+ export default BlogPost;
@@ -0,0 +1,31 @@
1
+ const meta = {
2
+ title: 'What is Reroute?',
3
+ description:
4
+ 'A dead simple file-based routing framework for building fullstack React apps on Bun and Elysia.',
5
+ date: '2025-10-22',
6
+ };
7
+
8
+ function BlogPost() {
9
+ return (
10
+ <div>
11
+ <h1>What is Reroute?</h1>
12
+ <p>
13
+ Reroute is a dead simple file-based routing framework for building
14
+ fullstack applications using React that runs on Bun and Elysia.
15
+ </p>
16
+ <p>
17
+ It features server-side rendering (SSR), automatic route generation from
18
+ your file structure, live reload in development, and zero configuration
19
+ needed to get started.
20
+ </p>
21
+ <p>
22
+ With built-in content collections, smart caching, and powerful React
23
+ hooks like useNavigate(), useData(), and useContent(), Reroute makes it
24
+ easy to build fast, SEO-friendly web applications with minimal setup.
25
+ </p>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ export default BlogPost;
31
+ export { meta };
@@ -1,8 +1,9 @@
1
1
  import { Link, useContent } from 'reroute-js/react';
2
2
 
3
3
  const meta = {
4
- title: 'Blog',
5
- description: 'Read our latest posts',
4
+ title: 'Blog Example',
5
+ description:
6
+ 'See how easy it is to build a blog with custom file-based routing!',
6
7
  };
7
8
 
8
9
  function BlogIndex() {
@@ -4,7 +4,7 @@ export default function HomePage() {
4
4
  return (
5
5
  <main style={{ padding: '2rem', maxWidth: '1200px', margin: '0 auto' }}>
6
6
  <h1>🏠 Welcome to Reroute!</h1>
7
- <p>This is your new blog built with Reroute.</p>
7
+ <p>This is the home page of your custom file-based routing system.</p>
8
8
 
9
9
  <section style={{ marginTop: '2rem' }}>
10
10
  <h2>Navigation</h2>
@@ -38,17 +38,6 @@ export default function HomePage() {
38
38
  >
39
39
  Blog
40
40
  </Link>
41
- <Link
42
- to='/blog/hello-world'
43
- style={{
44
- padding: '0.5rem 1rem',
45
- background: '#eee',
46
- textDecoration: 'none',
47
- borderRadius: '4px',
48
- }}
49
- >
50
- First Blog Post
51
- </Link>
52
41
  </nav>
53
42
  </section>
54
43
 
@@ -62,12 +51,11 @@ export default function HomePage() {
62
51
  >
63
52
  <h3>✨ Features</h3>
64
53
  <ul>
65
- <li>File-based routing</li>
54
+ <li>Zero-dependency file-based routing</li>
66
55
  <li>Client-side navigation (SPA)</li>
67
56
  <li>Server-side rendering (SSR)</li>
68
57
  <li>Type-safe route params</li>
69
58
  <li>Automatic route generation</li>
70
- <li>Content collections</li>
71
59
  </ul>
72
60
  </section>
73
61
  </main>
@@ -12,9 +12,9 @@ const app = new Elysia()
12
12
  minify: IS_PRODUCTION,
13
13
  }),
14
14
  )
15
- .get('/api/message', () => ({ message: 'Hello from server' }))
15
+ .get('/message', () => ({ message: 'Hello from server' }))
16
16
  .listen(Number(Bun.env.PORT || '3000'));
17
17
 
18
18
  console.log(
19
- `🦊 Reroute is running at ${app.server?.hostname}:${app.server?.port}`,
19
+ `🦊 Elysia Reroute is running at ${app.server?.hostname}:${app.server?.port} on ${Bun.env.NODE_ENV || 'development'}`,
20
20
  );
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.0.0",
4
+ "description": "A Reroute store application with Tailwind CSS v4",
5
+ "private": true,
6
+ "scripts": {
7
+ "start": "reroute gen && bun src/index.ts",
8
+ "dev": "reroute gen --watch & bun --watch src/index.ts",
9
+ "build": "reroute gen && bun build src/index.ts --target bun --compile --outfile ./dist/app"
10
+ },
11
+ "dependencies": {
12
+ "reroute-js": "latest",
13
+ "elysia": "^1.4.12",
14
+ "react": "^19.2.0",
15
+ "react-dom": "^19.2.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/bun": "latest",
19
+ "@types/react": "^19.2.2",
20
+ "@types/react-dom": "^19.2.2",
21
+ "@tailwindcss/cli": "^4.1.16",
22
+ "tailwindcss": "^4.1.16",
23
+ "typescript": "^5"
24
+ }
25
+ }
@@ -0,0 +1,17 @@
1
+ import { RerouteProvider } from 'reroute-js/react';
2
+ import { artifacts } from '../../.reroute';
3
+
4
+ interface AppProps {
5
+ pathname?: string;
6
+ }
7
+
8
+ export default function App({ pathname }: AppProps = {}) {
9
+ return (
10
+ <RerouteProvider
11
+ from={{
12
+ pathname,
13
+ ...artifacts,
14
+ }}
15
+ />
16
+ );
17
+ }
@@ -0,0 +1,40 @@
1
+ import { Link } from 'reroute-js/react';
2
+
3
+ export default function Header() {
4
+ return (
5
+ <header className='sticky top-0 z-50 bg-white border-b border-gray-200 shadow-sm'>
6
+ <div className='container-custom'>
7
+ <div className='flex items-center justify-between h-16'>
8
+ <Link
9
+ to='/'
10
+ className='flex items-center gap-2 text-2xl font-bold text-primary-600 hover:text-primary-700 transition-colors'
11
+ >
12
+ <span className='text-3xl'>🛒</span>
13
+ <span>Reroute Store</span>
14
+ </Link>
15
+
16
+ <nav className='flex items-center gap-8'>
17
+ <Link
18
+ to='/'
19
+ className='link-muted font-medium hover:text-primary-600'
20
+ >
21
+ Home
22
+ </Link>
23
+ <Link
24
+ to='/products'
25
+ className='link-muted font-medium hover:text-primary-600'
26
+ >
27
+ Products
28
+ </Link>
29
+ <Link
30
+ to='/categories'
31
+ className='link-muted font-medium hover:text-primary-600'
32
+ >
33
+ Categories
34
+ </Link>
35
+ </nav>
36
+ </div>
37
+ </div>
38
+ </header>
39
+ );
40
+ }
@@ -0,0 +1,51 @@
1
+ /** biome-ignore-all lint/a11y/noStaticElementInteractions: who cares, this is just an example */
2
+ import { Link } from 'reroute-js/react';
3
+ import type { Product } from '../lib/api';
4
+
5
+ interface ProductCardProps {
6
+ product: Product;
7
+ }
8
+
9
+ export default function ProductCard({ product }: ProductCardProps) {
10
+ return (
11
+ <div className='card card-hover group'>
12
+ <Link
13
+ to={`/products/${product.id}`}
14
+ className='block h-full no-underline text-inherit'
15
+ >
16
+ <div className='w-full h-60 bg-gray-50 flex items-center justify-center p-4'>
17
+ <img
18
+ src={product.image}
19
+ alt={product.title}
20
+ className='max-w-full max-h-full object-contain'
21
+ loading='lazy'
22
+ style={{ viewTransitionName: `product-image-${product.id}` }}
23
+ />
24
+ </div>
25
+
26
+ <div className='p-4 flex flex-col gap-2 flex-1'>
27
+ <div className='badge-primary'>{product.category}</div>
28
+
29
+ <h3 className='text-base font-semibold text-gray-900 leading-normal line-clamp-2 min-h-[3em] group-hover:text-primary-600 transition-colors'>
30
+ {product.title}
31
+ </h3>
32
+
33
+ <div className='flex justify-between items-center mt-auto pt-2'>
34
+ <span className='text-xl font-bold text-primary-600'>
35
+ ${product.price.toFixed(2)}
36
+ </span>
37
+
38
+ {product.rating && (
39
+ <div className='flex items-center gap-1 text-sm text-gray-600'>
40
+ <span>⭐</span>
41
+ <span>
42
+ {product.rating.rate} ({product.rating.count})
43
+ </span>
44
+ </div>
45
+ )}
46
+ </div>
47
+ </div>
48
+ </Link>
49
+ </div>
50
+ );
51
+ }
@@ -0,0 +1,17 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>{{PROJECT_NAME}}</title>
6
+
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <meta
9
+ name="description"
10
+ content="E-commerce store built with Reroute and FakeStoreAPI"
11
+ />
12
+ </head>
13
+ <body>
14
+ <div id="root"></div>
15
+ <script type="module" src="./index.tsx"></script>
16
+ </body>
17
+ </html>
@@ -0,0 +1,7 @@
1
+ import { hydrateRoot } from 'react-dom/client';
2
+ import App from './App';
3
+
4
+ const rootElement = document.getElementById('root');
5
+
6
+ // biome-ignore lint/style/noNonNullAssertion: All good
7
+ hydrateRoot(rootElement!, <App />);