create-react-starter-kit 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +0 -66
  2. package/dist/generator/dependencies.d.ts.map +1 -1
  3. package/dist/generator/dependencies.js +20 -2
  4. package/dist/generator/dependencies.js.map +1 -1
  5. package/dist/generator/index.d.ts.map +1 -1
  6. package/dist/generator/index.js +10 -2
  7. package/dist/generator/index.js.map +1 -1
  8. package/dist/templates/README.md.ejs +23 -29
  9. package/dist/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +17 -1
  10. package/dist/templates/__when_e2eTesting_playwright__/tests/e2e/counter.spec.ts.ejs +25 -0
  11. package/dist/templates/__when_e2eTesting_playwright__/tests/e2e/feedback.spec.ts.ejs +20 -0
  12. package/dist/templates/__when_e2eTesting_playwright__/tests/e2e/home.spec.ts.ejs +30 -3
  13. package/dist/templates/__when_e2eTesting_playwright__/tests/e2e/posts.spec.ts.ejs +17 -0
  14. package/dist/templates/__when_formatting_prettier__/.prettierignore +28 -0
  15. package/dist/templates/__when_linting_biome__/biome.json +12 -0
  16. package/dist/templates/__when_unitTesting_jest__/tests/App.test.tsx.ejs +10 -1
  17. package/dist/templates/__when_unitTesting_rtl__/tests/App.test.tsx.ejs +10 -1
  18. package/dist/templates/__when_unitTesting_vitest__/tests/counter.test.tsx.ejs +36 -0
  19. package/dist/templates/__when_unitTesting_vitest__/tests/feedback.test.tsx.ejs +49 -0
  20. package/dist/templates/__when_unitTesting_vitest__/tests/pages.test.tsx.ejs +26 -0
  21. package/dist/templates/__when_unitTesting_vitest__/tests/posts.test.tsx.ejs +58 -0
  22. package/dist/templates/__when_unitTesting_vitest__/tests/test-utils.tsx.ejs +50 -0
  23. package/dist/templates/__when_visualTesting_storybook__/.storybook/main.js.ejs +4 -1
  24. package/dist/templates/__when_visualTesting_storybook__/.storybook/preview.js.ejs +40 -0
  25. package/dist/templates/__when_visualTesting_storybook__/.storybook/preview.tsx.ejs +40 -0
  26. package/dist/templates/__when_visualTesting_storybook__/src/components/StackBadge.stories.tsx.ejs +35 -0
  27. package/dist/templates/__when_visualTesting_storybook__/src/components/ui/UiButton.stories.tsx.ejs +24 -1
  28. package/dist/templates/__when_visualTesting_storybook__/src/features/counter/CounterPanel.stories.tsx.ejs +25 -0
  29. package/dist/templates/__when_visualTesting_storybook__/src/features/feedback/FeedbackForm.stories.tsx.ejs +25 -0
  30. package/dist/templates/__when_visualTesting_storybook__/src/features/posts/PostsPanel.stories.tsx.ejs +25 -0
  31. package/dist/templates/__when_visualTesting_storybook__/src/routes/Pages.stories.tsx.ejs +42 -0
  32. package/dist/templates/docs/architecture.md.ejs +32 -34
  33. package/dist/templates/docs/getting-started.md.ejs +1 -1
  34. package/{templates/src/app → dist/templates/src}/App.tsx.ejs +34 -1
  35. package/dist/templates/src/components/layout/AppLayout.tsx.ejs +35 -12
  36. package/dist/templates/src/features/counter/CounterPanel.tsx.ejs +12 -6
  37. package/dist/templates/src/features/feedback/FeedbackForm.tsx.ejs +26 -7
  38. package/dist/templates/src/main.tsx.ejs +2 -2
  39. package/{templates/src/app → dist/templates/src}/providers.tsx.ejs +1 -2
  40. package/dist/templates/src/routes/AboutPage.tsx.ejs +11 -10
  41. package/dist/templates/src/routes/CounterPage.tsx.ejs +17 -0
  42. package/dist/templates/src/routes/FeedbackPage.tsx.ejs +21 -0
  43. package/dist/templates/src/routes/HomePage.tsx.ejs +43 -21
  44. package/dist/templates/src/routes/PostsPage.tsx.ejs +17 -0
  45. package/dist/templates/src/styles/global.css.ejs +14 -33
  46. package/package.json +1 -1
  47. package/templates/README.md.ejs +23 -29
  48. package/templates/__when_e2eTesting_playwright__/playwright.config.ts.ejs +17 -1
  49. package/templates/__when_e2eTesting_playwright__/tests/e2e/counter.spec.ts.ejs +25 -0
  50. package/templates/__when_e2eTesting_playwright__/tests/e2e/feedback.spec.ts.ejs +20 -0
  51. package/templates/__when_e2eTesting_playwright__/tests/e2e/home.spec.ts.ejs +30 -3
  52. package/templates/__when_e2eTesting_playwright__/tests/e2e/posts.spec.ts.ejs +17 -0
  53. package/templates/__when_formatting_prettier__/.prettierignore +28 -0
  54. package/templates/__when_linting_biome__/biome.json +12 -0
  55. package/templates/__when_unitTesting_jest__/tests/App.test.tsx.ejs +10 -1
  56. package/templates/__when_unitTesting_rtl__/tests/App.test.tsx.ejs +10 -1
  57. package/templates/__when_unitTesting_vitest__/tests/counter.test.tsx.ejs +36 -0
  58. package/templates/__when_unitTesting_vitest__/tests/feedback.test.tsx.ejs +49 -0
  59. package/templates/__when_unitTesting_vitest__/tests/pages.test.tsx.ejs +26 -0
  60. package/templates/__when_unitTesting_vitest__/tests/posts.test.tsx.ejs +58 -0
  61. package/templates/__when_unitTesting_vitest__/tests/test-utils.tsx.ejs +50 -0
  62. package/templates/__when_visualTesting_storybook__/.storybook/main.js.ejs +4 -1
  63. package/templates/__when_visualTesting_storybook__/.storybook/preview.js.ejs +40 -0
  64. package/templates/__when_visualTesting_storybook__/.storybook/preview.tsx.ejs +40 -0
  65. package/templates/__when_visualTesting_storybook__/src/components/StackBadge.stories.tsx.ejs +35 -0
  66. package/templates/__when_visualTesting_storybook__/src/components/ui/UiButton.stories.tsx.ejs +24 -1
  67. package/templates/__when_visualTesting_storybook__/src/features/counter/CounterPanel.stories.tsx.ejs +25 -0
  68. package/templates/__when_visualTesting_storybook__/src/features/feedback/FeedbackForm.stories.tsx.ejs +25 -0
  69. package/templates/__when_visualTesting_storybook__/src/features/posts/PostsPanel.stories.tsx.ejs +25 -0
  70. package/templates/__when_visualTesting_storybook__/src/routes/Pages.stories.tsx.ejs +42 -0
  71. package/templates/docs/architecture.md.ejs +32 -34
  72. package/templates/docs/getting-started.md.ejs +1 -1
  73. package/{dist/templates/src/app → templates/src}/App.tsx.ejs +34 -1
  74. package/templates/src/components/layout/AppLayout.tsx.ejs +35 -12
  75. package/templates/src/features/counter/CounterPanel.tsx.ejs +12 -6
  76. package/templates/src/features/feedback/FeedbackForm.tsx.ejs +26 -7
  77. package/templates/src/main.tsx.ejs +2 -2
  78. package/{dist/templates/src/app → templates/src}/providers.tsx.ejs +1 -2
  79. package/templates/src/routes/AboutPage.tsx.ejs +11 -10
  80. package/templates/src/routes/CounterPage.tsx.ejs +17 -0
  81. package/templates/src/routes/FeedbackPage.tsx.ejs +21 -0
  82. package/templates/src/routes/HomePage.tsx.ejs +43 -21
  83. package/templates/src/routes/PostsPage.tsx.ejs +17 -0
  84. package/templates/src/styles/global.css.ejs +14 -33
  85. package/dist/templates/__when_unitTesting_vitest__/tests/App.test.tsx.ejs +0 -16
  86. package/dist/templates/__when_visualTesting_storybook__/.storybook/preview.js +0 -8
  87. package/dist/templates/src/components/ui/Toast.tsx.ejs +0 -61
  88. package/templates/__when_unitTesting_vitest__/tests/App.test.tsx.ejs +0 -16
  89. package/templates/__when_visualTesting_storybook__/.storybook/preview.js +0 -8
  90. package/templates/src/components/ui/Toast.tsx.ejs +0 -61
@@ -0,0 +1,42 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { HomePage } from '@/routes/HomePage';
3
+ import { AboutPage } from '@/routes/AboutPage';
4
+ import { CounterPage } from '@/routes/CounterPage';
5
+ import { PostsPage } from '@/routes/PostsPage';
6
+ import { FeedbackPage } from '@/routes/FeedbackPage';
7
+
8
+ const meta = {
9
+ title: 'Pages/Routes',
10
+ parameters: {
11
+ layout: 'fullscreen',
12
+ docs: {
13
+ description: {
14
+ component:
15
+ 'Route-level pages composed from feature panels. Useful for visual review of layout and copy.',
16
+ },
17
+ },
18
+ },
19
+ } satisfies Meta;
20
+
21
+ export default meta;
22
+ type Story = StoryObj;
23
+
24
+ export const Home: Story = {
25
+ render: () => <HomePage />,
26
+ };
27
+
28
+ export const About: Story = {
29
+ render: () => <AboutPage />,
30
+ };
31
+
32
+ export const Counter: Story = {
33
+ render: () => <CounterPage />,
34
+ };
35
+
36
+ export const Posts: Story = {
37
+ render: () => <PostsPage />,
38
+ };
39
+
40
+ export const Feedback: Story = {
41
+ render: () => <FeedbackPage />,
42
+ };
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Why this stack
4
4
 
5
- You selected a coherent default-friendly stack during scaffolding. The generator wires providers, scripts, and a demo page so packages are not orphaned in `package.json`.
5
+ You selected a coherent default-friendly stack during scaffolding. The generator wires providers, scripts, and demo routes so packages are not orphaned in `package.json`.
6
6
 
7
7
  | Concern | Choice | Role |
8
8
  |---------|--------|------|
@@ -19,56 +19,54 @@ You selected a coherent default-friendly stack during scaffolding. The generator
19
19
 
20
20
  ## Folder organization (feature-based)
21
21
 
22
- This scaffold uses a **feature-based / domain-driven** layout — the pattern most large React teams settle on as apps grow (similar in spirit to how companies structure product domains: keep code that changes together close together).
23
-
24
22
  ```text
25
23
  src/
26
- app/ # bootstrap, providers, root App shell
27
- assets/ # images, fonts, static media imported from code
28
- components/ # shared, feature-agnostic UI (layout/, ui/)
29
- config/ # env helpers and app-level constants
30
- features/ # domain modules (counter, posts, feedback, …)
31
- hooks/ # shared hooks
32
- lib/ # third-party wrappers / cross-cutting helpers
33
- routes/ # route-level pages
34
- services/ # API clients / transport
35
- store/ # global client state (when selected)
36
- styles/ # global styles / tokens
37
- types/ # shared TypeScript types
38
- utils/ # pure helpers
24
+ main.<%= jsxExt %> # bootstrap + render
25
+ providers.<%= jsxExt %>
26
+ App.<%= jsxExt %> # route tree
27
+ assets/
28
+ components/ # shared UI (layout/, ui/)
29
+ config/
30
+ features/ # domain modules
31
+ hooks/
32
+ lib/
33
+ routes/ # / /counter /posts /feedback /about
34
+ services/
35
+ store/
36
+ styles/
37
+ types/
38
+ utils/
39
39
  ```
40
40
 
41
41
  **Rules of thumb**
42
42
 
43
43
  - Put new product work in `src/features/<domain>/`.
44
44
  - Keep `components/` dumb and reusable.
45
- - Import with the `@/` alias (e.g. `import { apiGet } from '@/services/api'`).
45
+ - Import with the `@/` alias.
46
46
  - Prefer a feature's public surface over deep cross-feature imports.
47
47
 
48
- ## Path aliases
48
+ ## Routes
49
49
 
50
- `@/*` maps to `src/*` in TypeScript/JS config and in your bundler (`<%= stackLabels.buildTool %>`).
50
+ | Path | Purpose |
51
+ |------|---------|
52
+ | `/` | Home overview |
53
+ | `/counter` | Client state demo |
54
+ | `/posts` | Server state / data fetching |
55
+ | `/feedback` | Forms + validation |
56
+ | `/about` | Orientation |
51
57
 
52
- ```ts
53
- import { HomePage } from '@/routes/HomePage';
54
- import { useToast } from '@/components/ui/Toast';
55
- ```
58
+ ## Path aliases
59
+
60
+ `@/*` maps to `src/*` in TypeScript/JS config and in your bundler.
56
61
 
57
62
  ## Runtime composition
58
63
 
59
- `src/main.<%= jsxExt %>` → providers (`src/app/providers.<%= jsxExt %>`) router (`src/app/App.<%= jsxExt %>`) → layouts/pages.
64
+ `src/main.<%= jsxExt %>` → `AppProviders` → `App` (router) → layout → pages.
60
65
 
61
- Providers are ordered deliberately: state → server cache → UI theme → toasts.
66
+ Providers are ordered: client state → server cache → UI theme.
62
67
 
63
68
  ## Boundaries
64
69
 
65
- - **UI** must not call `fetch`/`axios` directly in deep leaves — go through `services/`.
66
- - **Schemas** live in `lib/validation` so forms and API payloads share rules.
70
+ - **UI** should call HTTP through `services/`.
71
+ - **Schemas** live in `lib/validation`.
67
72
  - **Env access** stays in `config/`, `services/`, and `lib/monitoring`.
68
-
69
- ## Production readiness patterns
70
-
71
- - Code-split heavy routes when the app grows.
72
- - Fail closed on missing monitoring DSN (already gated).
73
- - Keep lint/format/test scripts green in CI.
74
- - Document public env vars only in `.env.example`.
@@ -38,7 +38,7 @@ Only variables prefixed for your bundler (e.g. `VITE_`) are exposed to the brows
38
38
  ## Extending the app
39
39
 
40
40
  - Add a domain under `src/features/<name>/`.
41
- - Register routes in `src/app/App.<%= jsxExt %>`.
41
+ - Register routes in `src/App.<%= jsxExt %>`.
42
42
  - Put shared UI in `src/components/`.
43
43
  - Put HTTP clients in `src/services/`.
44
44
  - Import with `@/` (maps to `src/`), e.g. `import { env } from '@/config/env'`.
@@ -3,6 +3,9 @@ import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
3
3
  import { AppLayout } from '@/components/layout/AppLayout';
4
4
  import { HomePage } from '@/routes/HomePage';
5
5
  import { AboutPage } from '@/routes/AboutPage';
6
+ import { CounterPage } from '@/routes/CounterPage';
7
+ import { PostsPage } from '@/routes/PostsPage';
8
+ import { FeedbackPage } from '@/routes/FeedbackPage';
6
9
 
7
10
  export function App() {
8
11
  return (
@@ -11,6 +14,9 @@ export function App() {
11
14
  <Route element={<AppLayout />}>
12
15
  <Route path="/" element={<HomePage />} />
13
16
  <Route path="/about" element={<AboutPage />} />
17
+ <Route path="/counter" element={<CounterPage />} />
18
+ <Route path="/posts" element={<PostsPage />} />
19
+ <Route path="/feedback" element={<FeedbackPage />} />
14
20
  <Route path="*" element={<Navigate to="/" replace />} />
15
21
  </Route>
16
22
  </Routes>
@@ -28,6 +34,9 @@ import {
28
34
  import { AppLayout } from '@/components/layout/AppLayout';
29
35
  import { HomePage } from '@/routes/HomePage';
30
36
  import { AboutPage } from '@/routes/AboutPage';
37
+ import { CounterPage } from '@/routes/CounterPage';
38
+ import { PostsPage } from '@/routes/PostsPage';
39
+ import { FeedbackPage } from '@/routes/FeedbackPage';
31
40
 
32
41
  function RootComponent() {
33
42
  return (
@@ -53,7 +62,31 @@ const aboutRoute = createRoute({
53
62
  component: AboutPage,
54
63
  });
55
64
 
56
- const routeTree = rootRoute.addChildren([indexRoute, aboutRoute]);
65
+ const counterRoute = createRoute({
66
+ getParentRoute: () => rootRoute,
67
+ path: '/counter',
68
+ component: CounterPage,
69
+ });
70
+
71
+ const postsRoute = createRoute({
72
+ getParentRoute: () => rootRoute,
73
+ path: '/posts',
74
+ component: PostsPage,
75
+ });
76
+
77
+ const feedbackRoute = createRoute({
78
+ getParentRoute: () => rootRoute,
79
+ path: '/feedback',
80
+ component: FeedbackPage,
81
+ });
82
+
83
+ const routeTree = rootRoute.addChildren([
84
+ indexRoute,
85
+ aboutRoute,
86
+ counterRoute,
87
+ postsRoute,
88
+ feedbackRoute,
89
+ ]);
57
90
 
58
91
  const router = createRouter({ routeTree });
59
92
 
@@ -13,6 +13,14 @@ interface AppLayoutProps {
13
13
  }
14
14
  <% } -%>
15
15
 
16
+ const links = [
17
+ { to: '/', label: 'Home', end: true },
18
+ { to: '/counter', label: 'Counter' },
19
+ { to: '/posts', label: 'Posts' },
20
+ { to: '/feedback', label: 'Feedback' },
21
+ { to: '/about', label: 'About' },
22
+ ] as const;
23
+
16
24
  export function AppLayout(<% if (router === 'tanstack-router') { %>{ children }<% if (isTypeScript) { %>: AppLayoutProps<% } %><% } %>) {
17
25
  <% if (router === 'tanstack-router') { -%>
18
26
  const pathname = useRouterState({ select: (s) => s.location.pathname });
@@ -30,19 +38,34 @@ export function AppLayout(<% if (router === 'tanstack-router') { %>{ children }<
30
38
  </div>
31
39
  <nav className="app-nav" aria-label="Primary">
32
40
  <% if (router === 'react-router') { -%>
33
- <NavLink to="/" end className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}>
34
- Home
35
- </NavLink>
36
- <NavLink to="/about" className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}>
37
- About
38
- </NavLink>
41
+ {links.map((link) => (
42
+ <NavLink
43
+ key={link.to}
44
+ to={link.to}
45
+ end={'end' in link ? link.end : false}
46
+ className={({ isActive }) => (isActive ? 'nav-link active' : 'nav-link')}
47
+ >
48
+ {link.label}
49
+ </NavLink>
50
+ ))}
39
51
  <% } else { -%>
40
- <Link to="/" className={pathname === '/' ? 'nav-link active' : 'nav-link'}>
41
- Home
42
- </Link>
43
- <Link to="/about" className={pathname === '/about' ? 'nav-link active' : 'nav-link'}>
44
- About
45
- </Link>
52
+ {links.map((link) => (
53
+ <Link
54
+ key={link.to}
55
+ to={link.to}
56
+ className={
57
+ link.to === '/'
58
+ ? pathname === '/'
59
+ ? 'nav-link active'
60
+ : 'nav-link'
61
+ : pathname.startsWith(link.to)
62
+ ? 'nav-link active'
63
+ : 'nav-link'
64
+ }
65
+ >
66
+ {link.label}
67
+ </Link>
68
+ ))}
46
69
  <% } -%>
47
70
  </nav>
48
71
  </header>
@@ -13,10 +13,8 @@ import { counterAtom } from '@/store';
13
13
  import { useAppState } from '@/store/context';
14
14
  <% } -%>
15
15
  import { UiButton } from '@/components/ui/UiButton';
16
- import { useToast } from '@/components/ui/Toast';
17
16
 
18
17
  export function CounterPanel() {
19
- const { push } = useToast();
20
18
  <% if (stateManagement === 'redux') { -%>
21
19
  const value = useSelector((state<% if (isTypeScript) { %>: RootState<% } %>) => state.counter.value);
22
20
  const dispatch = useDispatch();
@@ -48,12 +46,20 @@ export function CounterPanel() {
48
46
  <p><%= stackLabels.stateManagement %> example — local interactive counter.</p>
49
47
  </header>
50
48
  <% if (stateManagement === 'none') { -%>
51
- <p className="muted">Client state library was set to None. Add one later under <code>src/store</code>.</p>
49
+ <p className="muted">
50
+ Client state library was set to None. Add one later under <code>src/store</code>.
51
+ </p>
52
52
  <% } else { -%>
53
53
  <div className="counter">
54
- <UiButton onClick={() => { onDec(); push('Decremented', 'info'); }}>−</UiButton>
55
- <strong className="counter-value" aria-live="polite">{value}</strong>
56
- <UiButton onClick={() => { onInc(); push('Incremented', 'success'); }}>+</UiButton>
54
+ <UiButton aria-label="Decrement" onClick={onDec}>
55
+
56
+ </UiButton>
57
+ <strong className="counter-value" data-testid="counter-value" aria-live="polite">
58
+ {value}
59
+ </strong>
60
+ <UiButton aria-label="Increment" onClick={onInc}>
61
+ +
62
+ </UiButton>
57
63
  </div>
58
64
  <% } -%>
59
65
  <p className="panel-note">
@@ -1,3 +1,4 @@
1
+ import { useState } from 'react';
1
2
  <% if (forms === 'react-hook-form') { -%>
2
3
  import { useForm } from 'react-hook-form';
3
4
  <% if (validation === 'zod') { -%>
@@ -25,7 +26,6 @@ import { feedbackSchema } from '@/lib/validation';
25
26
  import type { FeedbackValues } from '@/lib/validation';
26
27
  <% } -%>
27
28
  <% } else { -%>
28
- import { useState } from 'react';
29
29
  <% if (validation === 'none') { -%>
30
30
  import { validateFeedback<% if (isTypeScript) { %>, type FeedbackValues<% } %> } from '@/lib/validation';
31
31
  <% } else { -%>
@@ -33,7 +33,6 @@ import { feedbackSchema<% if (isTypeScript) { %>, type FeedbackValues<% } %> } f
33
33
  <% } -%>
34
34
  <% } -%>
35
35
  import { createFeedback } from '@/services/api';
36
- import { useToast } from '@/components/ui/Toast';
37
36
  import { UiButton } from '@/components/ui/UiButton';
38
37
 
39
38
  const defaults<% if (isTypeScript) { %>: FeedbackValues<% } %> = {
@@ -43,7 +42,7 @@ const defaults<% if (isTypeScript) { %>: FeedbackValues<% } %> = {
43
42
  };
44
43
 
45
44
  export function FeedbackForm() {
46
- const { push } = useToast();
45
+ const [status, setStatus] = useState<% if (isTypeScript) { %><string | null><% } %>(null);
47
46
 
48
47
  <% if (forms === 'react-hook-form') { -%>
49
48
  const {
@@ -71,7 +70,7 @@ export function FeedbackForm() {
71
70
  }
72
71
  <% } -%>
73
72
  await createFeedback(values);
74
- push('Feedback submitted (demo API)', 'success');
73
+ setStatus('Feedback submitted successfully (demo API).');
75
74
  reset();
76
75
  };
77
76
 
@@ -107,6 +106,11 @@ export function FeedbackForm() {
107
106
  {isSubmitting ? 'Sending…' : 'Send feedback'}
108
107
  </UiButton>
109
108
  </form>
109
+ {status && (
110
+ <p className="success-text" role="status">
111
+ {status}
112
+ </p>
113
+ )}
110
114
  <p className="panel-note">
111
115
  <strong>When to use:</strong> complex forms with validation and async submit.
112
116
  <br />
@@ -137,7 +141,7 @@ export function FeedbackForm() {
137
141
  feedbackSchema.parse(values);
138
142
  <% } -%>
139
143
  await createFeedback(values);
140
- push('Feedback submitted (demo API)', 'success');
144
+ setStatus('Feedback submitted successfully (demo API).');
141
145
  helpers.resetForm();
142
146
  }}
143
147
  >
@@ -164,6 +168,11 @@ export function FeedbackForm() {
164
168
  </Form>
165
169
  )}
166
170
  </Formik>
171
+ {status && (
172
+ <p className="success-text" role="status">
173
+ {status}
174
+ </p>
175
+ )}
167
176
  <p className="panel-note">
168
177
  <strong>When to use:</strong> form-heavy screens with Formik's render/props model.
169
178
  <br />
@@ -187,7 +196,7 @@ export function FeedbackForm() {
187
196
  if (result.error) throw result.error;
188
197
  <% } -%>
189
198
  await createFeedback(value);
190
- push('Feedback submitted (demo API)', 'success');
199
+ setStatus('Feedback submitted successfully (demo API).');
191
200
  form.reset();
192
201
  },
193
202
  });
@@ -255,6 +264,11 @@ export function FeedbackForm() {
255
264
  </form.Field>
256
265
  <UiButton type="submit">Send feedback</UiButton>
257
266
  </form>
267
+ {status && (
268
+ <p className="success-text" role="status">
269
+ {status}
270
+ </p>
271
+ )}
258
272
  <p className="panel-note">
259
273
  <strong>When to use:</strong> headless, type-safe forms with fine-grained reactivity.
260
274
  <br />
@@ -315,7 +329,7 @@ export function FeedbackForm() {
315
329
  setIsSubmitting(true);
316
330
  try {
317
331
  await createFeedback(values);
318
- push('Feedback submitted (demo API)', 'success');
332
+ setStatus('Feedback submitted successfully (demo API).');
319
333
  setValues(defaults);
320
334
  } finally {
321
335
  setIsSubmitting(false);
@@ -362,6 +376,11 @@ export function FeedbackForm() {
362
376
  {isSubmitting ? 'Sending…' : 'Send feedback'}
363
377
  </UiButton>
364
378
  </form>
379
+ {status && (
380
+ <p className="success-text" role="status">
381
+ {status}
382
+ </p>
383
+ )}
365
384
  <p className="panel-note">
366
385
  <strong>When to use:</strong> simple forms without a dedicated form library.
367
386
  <br />
@@ -1,7 +1,7 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
- import { AppProviders } from '@/app/providers';
4
- import { App } from '@/app/App';
3
+ import { AppProviders } from '@/providers';
4
+ import { App } from '@/App';
5
5
  import { initMonitoring } from '@/lib/monitoring';
6
6
  import '@/styles/global.<%= styling === 'sass' && uiLibrary !== 'shadcn' ? 'scss' : 'css' %>';
7
7
 
@@ -24,7 +24,6 @@ import { ChakraProvider, defaultSystem } from '@chakra-ui/react';
24
24
  <% if (uiLibrary === 'mui') { -%>
25
25
  import { CssBaseline, ThemeProvider, createTheme } from '@mui/material';
26
26
  <% } -%>
27
- import { ToastProvider } from '@/components/ui/Toast';
28
27
 
29
28
  <% if (serverState === 'tanstack-query') { -%>
30
29
  const queryClient = new QueryClient({
@@ -89,5 +88,5 @@ export function AppProviders({ children }<% if (isTypeScript) { %>: AppProviders
89
88
  tree = <ChakraProvider value={defaultSystem}>{tree}</ChakraProvider>;
90
89
  <% } -%>
91
90
 
92
- return <ToastProvider>{tree}</ToastProvider>;
91
+ return <>{tree}</>;
93
92
  }
@@ -4,26 +4,27 @@ export function AboutPage() {
4
4
  <h1>About this starter</h1>
5
5
  <p>
6
6
  Generated with <strong>create-react-starter-kit</strong> using a scalable{' '}
7
- <strong>feature-based</strong> layout (domain modules under <code>src/features</code>)
8
- the same organizational approach used across large production React codebases.
7
+ <strong>feature-based</strong> layout (domain modules under <code>src/features</code>).
9
8
  </p>
10
9
  <ul className="about-list">
11
10
  <li>
12
- <strong>Extend features</strong> under <code>src/features/&lt;name&gt;/</code> keep
13
- domain logic close to the UI that owns it.
11
+ <strong>Routes:</strong> <code>/</code>, <code>/counter</code>, <code>/posts</code>,{' '}
12
+ <code>/feedback</code>, <code>/about</code>
14
13
  </li>
15
14
  <li>
16
- <strong>Shared primitives</strong> live in <code>src/components/</code> and{' '}
17
- <code>src/lib/</code>. Prefer imports via the <code>@/</code> path alias.
15
+ <strong>Extend features</strong> under <code>src/features/&lt;name&gt;/</code>.
18
16
  </li>
19
17
  <li>
20
- <strong>Docs</strong> in <code>docs/</code> explain architecture, testing, and production
21
- habits for this stack.
18
+ <strong>Shared UI</strong> lives in <code>src/components/</code>. Prefer the{' '}
19
+ <code>@/</code> path alias.
20
+ </li>
21
+ <li>
22
+ <strong>Bootstrap</strong> is in <code>src/main.<%= jsxExt %></code> with providers in{' '}
23
+ <code>src/providers.<%= jsxExt %></code> and routing in <code>src/App.<%= jsxExt %></code>.
22
24
  </li>
23
25
  </ul>
24
26
  <p>
25
- Prefer reading <code>docs/getting-started.md</code> next, then{' '}
26
- <code>docs/selected-stack.md</code> for library-specific guidance.
27
+ Read <code>docs/getting-started.md</code> next, then <code>docs/selected-stack.md</code>.
27
28
  </p>
28
29
  </div>
29
30
  );
@@ -0,0 +1,17 @@
1
+ import { CounterPanel } from '@/features/counter/CounterPanel';
2
+
3
+ export function CounterPage() {
4
+ return (
5
+ <div className="page">
6
+ <header className="page-intro">
7
+ <p className="eyebrow">Client state</p>
8
+ <h1>Counter</h1>
9
+ <p className="lede">
10
+ Interactive counter wired to <strong><%= stackLabels.stateManagement %></strong>. Use this
11
+ pattern for UI state that is not server cache.
12
+ </p>
13
+ </header>
14
+ <CounterPanel />
15
+ </div>
16
+ );
17
+ }
@@ -0,0 +1,21 @@
1
+ import { FeedbackForm } from '@/features/feedback/FeedbackForm';
2
+
3
+ export function FeedbackPage() {
4
+ return (
5
+ <div className="page">
6
+ <header className="page-intro">
7
+ <p className="eyebrow">Forms</p>
8
+ <h1>Feedback</h1>
9
+ <p className="lede">
10
+ Submit demo feedback with <strong><%= stackLabels.forms %></strong>
11
+ <% if (validation !== 'none') { -%>
12
+ {' '}
13
+ and <strong><%= stackLabels.validation %></strong> validation
14
+ <% } -%>
15
+ .
16
+ </p>
17
+ </header>
18
+ <FeedbackForm />
19
+ </div>
20
+ );
21
+ }
@@ -1,29 +1,45 @@
1
- import { CounterPanel } from '@/features/counter/CounterPanel';
2
- import { PostsPanel } from '@/features/posts/PostsPanel';
3
- import { FeedbackForm } from '@/features/feedback/FeedbackForm';
1
+ <% if (router === 'react-router') { -%>
2
+ import { Link } from 'react-router-dom';
3
+ <% } else { -%>
4
+ import { Link } from '@tanstack/react-router';
5
+ <% } -%>
4
6
  import { StackBadge } from '@/components/StackBadge';
5
- import { useToast } from '@/components/ui/Toast';
6
- import { UiButton } from '@/components/ui/UiButton';
7
7
 
8
- export function HomePage() {
9
- const { push } = useToast();
8
+ const demos = [
9
+ {
10
+ to: '/counter',
11
+ title: 'Counter',
12
+ body: 'Client state demo with <%= stackLabels.stateManagement %>.',
13
+ },
14
+ {
15
+ to: '/posts',
16
+ title: 'Posts',
17
+ body: 'Server state / data fetching with <%= stackLabels.serverState %>.',
18
+ },
19
+ {
20
+ to: '/feedback',
21
+ title: 'Feedback',
22
+ body: 'Forms and validation with <%= stackLabels.forms %><% if (validation !== 'none') { %> + <%= stackLabels.validation %><% } %>.',
23
+ },
24
+ ] as const;
10
25
 
26
+ export function HomePage() {
11
27
  return (
12
28
  <div className="page home-page">
13
29
  <section className="hero">
14
30
  <p className="eyebrow">Starter workspace</p>
15
31
  <h1><%= projectName %></h1>
16
32
  <p className="lede">
17
- A minimal, production-shaped React app wired for your selected stack. Explore the panels
18
- below — each one is a real usage example, not a placeholder.
33
+ A production-shaped React starter. Open each demo route to explore client state, server
34
+ data, and forms — each page is a real working example for your selected stack.
19
35
  </p>
20
36
  <div className="hero-actions">
21
- <UiButton onClick={() => push('Toast system is ready', 'success')}>
22
- Show toast
23
- </UiButton>
24
- <a className="text-link" href="/about">
25
- Read about the stack
26
- </a>
37
+ <Link className="ui-button" to="/counter">
38
+ Open counter demo
39
+ </Link>
40
+ <Link className="text-link" to="/about">
41
+ About this starter
42
+ </Link>
27
43
  </div>
28
44
  <div className="stack-row">
29
45
  <StackBadge label="Build" value="<%= stackLabels.buildTool %>" />
@@ -35,12 +51,18 @@ export function HomePage() {
35
51
  </div>
36
52
  </section>
37
53
 
38
- <section className="panels">
39
- <% if (stateManagement !== 'none') { -%>
40
- <CounterPanel />
41
- <% } -%>
42
- <PostsPanel />
43
- <FeedbackForm />
54
+ <section className="panels" aria-label="Demo routes">
55
+ {demos.map((demo) => (
56
+ <article key={demo.to} className="panel">
57
+ <header className="panel-header">
58
+ <h2>{demo.title}</h2>
59
+ <p>{demo.body}</p>
60
+ </header>
61
+ <Link className="text-link" to={demo.to}>
62
+ Go to {demo.title.toLowerCase()} →
63
+ </Link>
64
+ </article>
65
+ ))}
44
66
  </section>
45
67
  </div>
46
68
  );
@@ -0,0 +1,17 @@
1
+ import { PostsPanel } from '@/features/posts/PostsPanel';
2
+
3
+ export function PostsPage() {
4
+ return (
5
+ <div className="page">
6
+ <header className="page-intro">
7
+ <p className="eyebrow">Server state</p>
8
+ <h1>Posts</h1>
9
+ <p className="lede">
10
+ Fetches sample posts via <strong><%= stackLabels.serverState === 'None' ? stackLabels.apiLayer : stackLabels.serverState %></strong>{' '}
11
+ and <strong><%= stackLabels.apiLayer %></strong>.
12
+ </p>
13
+ </header>
14
+ <PostsPanel />
15
+ </div>
16
+ );
17
+ }