create-aziz-app 3.1.0 → 3.1.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.
@@ -1,667 +1,402 @@
1
- # ⚡ Aziz.js
2
-
3
- A lightweight Next.js clone with file-based routing, React hooks, server-side API routes, and ES module imports — **zero dependencies, instant startup**.
4
-
5
- ```
6
- Next.js: ~400KB + 20 dependencies + 10s startup
7
- Aziz.js: ~50KB + 0 dependencies + <1s startup
8
- ```
9
-
10
- ---
11
-
12
- ## Quick Start
13
-
14
- ```bash
15
- npx create-aziz-app my-app
16
- cd my-app
17
- npm run dev
18
- ```
19
-
20
- Open [http://localhost:3000](http://localhost:3000) — that's it.
21
-
22
- ---
23
-
24
- ## Project Structure
25
-
26
- ```
27
- my-app/
28
- ├── index.html ← entry point (don't touch)
29
- │
30
- ├── app/ ← your pages and layouts
31
- │ ├── layout.js ← root layout (navbar, footer, etc.)
32
- │ ├── page.js ← route: /
33
- │ ├── about/
34
- │ │ └── page.js ← route: /about
35
- │ └── blog/
36
- │ └── [slug]/
37
- │ └── page.js ← route: /blog/:slug (dynamic)
38
- │
39
- ├── api/ ← server-side API routes (Node.js)
40
- │ ├── users.js ← route: /api/users
41
- │ └── hello.js ← route: /api/hello
42
- │
43
- ├── components/ ← shared UI components
44
- │ ├── Navbar.js
45
- │ └── Footer.js
46
- │
47
- ├── lib/ ← utilities, database connections
48
- │ └── db.js
49
- │
50
- ├── models/ ← data models
51
- │ └── User.js
52
- │
53
- └── styles/
54
- └── globals.css
55
- ```
56
-
57
- ---
58
-
59
- ## Pages
60
-
61
- Every `page.js` inside `app/` becomes a route. Export a default function — no imports needed.
62
-
63
- ```js
64
- // app/about/page.js → route: /about
65
- export default function Page() {
66
- return (
67
- <div>
68
- <h1>About</h1>
69
- <p>Hello from the about page!</p>
70
- </div>
71
- );
72
- }
73
- ```
74
-
75
- ### Route mapping
76
-
77
- | File | Route |
78
- |------|-------|
79
- | `app/page.js` | `/` |
80
- | `app/about/page.js` | `/about` |
81
- | `app/blog/page.js` | `/blog` |
82
- | `app/blog/[slug]/page.js` | `/blog/:slug` |
83
- | `app/blog/[...slug]/page.js` | `/blog/*` (catch-all) |
84
-
85
- ### Creating a new page
86
-
87
- Just create the file — no registration needed:
88
-
89
- ```bash
90
- mkdir app/dashboard
91
- touch app/dashboard/page.js
92
- ```
93
-
94
- ```js
95
- // app/dashboard/page.js → route: /dashboard
96
- export default function Page() {
97
- return <h1>Dashboard</h1>;
98
- }
99
- ```
100
-
101
- ---
102
-
103
- ## Layouts
104
-
105
- `layout.js` wraps all pages at the same level and below. The root `app/layout.js` wraps every page.
106
-
107
- ```js
108
- // app/layout.js
109
- import Navbar from '/components/Navbar.js';
110
- import Footer from '/components/Footer.js';
111
-
112
- export default function Layout({ children }) {
113
- return (
114
- <div>
115
- <Navbar />
116
- <main>{children}</main>
117
- <Footer />
118
- </div>
119
- );
120
- }
121
- ```
122
-
123
- ### Nested layouts
124
-
125
- ```js
126
- // app/dashboard/layout.js — wraps all /dashboard/* pages
127
- import Sidebar from '/components/Sidebar.js';
128
-
129
- export default function Layout({ children }) {
130
- return (
131
- <div style={{ display: 'flex' }}>
132
- <Sidebar />
133
- <div style={{ flex: 1 }}>{children}</div>
134
- </div>
135
- );
136
- }
137
- ```
138
-
139
- ---
140
-
141
- ## Navigation
142
-
143
- ### navigate()
144
-
145
- ```js
146
- navigate('/about');
147
- navigate('/blog/my-post');
148
- navigate('/');
149
- ```
150
-
151
- ### Link component
152
-
153
- ```js
154
- <Link href="/about">About</Link>
155
- <Link href="/blog/hello" className="nav-link">Blog Post</Link>
156
- ```
157
-
158
- ### useRouter hook
159
-
160
- ```js
161
- export default function Page() {
162
- const router = useRouter();
163
-
164
- return (
165
- <div>
166
- <p>Current path: {router.pathname}</p>
167
- <button onClick={() => router.push('/about')}>Go to About</button>
168
- <button onClick={() => router.back()}>Back</button>
169
- </div>
170
- );
171
- }
172
- ```
173
-
174
- ### useParams — dynamic routes
175
-
176
- ```js
177
- // app/blog/[slug]/page.js
178
- export default function Page() {
179
- const { slug } = useParams();
180
- return <h1>Post: {slug}</h1>;
181
- }
182
- ```
183
-
184
- ### useSearchParams — query strings
185
-
186
- ```js
187
- // URL: /search?q=hello&page=2
188
- export default function Page() {
189
- const searchParams = useSearchParams();
190
- const query = searchParams.get('q'); // "hello"
191
- const page = searchParams.get('page'); // "2"
192
-
193
- return <p>Searching for: {query}</p>;
194
- }
195
- ```
196
-
197
- ---
198
-
199
- ## React Hooks
200
-
201
- All hooks work exactly like React — **no imports needed**.
202
-
203
- ### useState
204
-
205
- ```js
206
- const [count, setCount] = useState(0);
207
- const [user, setUser] = useState(null);
208
- const [items, setItems] = useState([]);
209
-
210
- setCount(prev => prev + 1);
211
- ```
212
-
213
- ### useEffect
214
-
215
- ```js
216
- useEffect(() => {
217
- console.log('mounted');
218
- }, []);
219
-
220
- useEffect(() => {
221
- document.title = `Count: ${count}`;
222
- }, [count]);
223
-
224
- useEffect(() => {
225
- const timer = setInterval(() => tick(), 1000);
226
- return () => clearInterval(timer);
227
- }, []);
228
- ```
229
-
230
- ### useMemo
231
-
232
- ```js
233
- const total = useMemo(() => {
234
- return items.filter(i => i.active).length;
235
- }, [items]);
236
- ```
237
-
238
- ### useCallback
239
-
240
- ```js
241
- const handleClick = useCallback(() => {
242
- doSomething(id);
243
- }, [id]);
244
- ```
245
-
246
- ### useRef
247
-
248
- ```js
249
- const inputRef = useRef(null);
250
-
251
- <input ref={inputRef} type="text" />
252
-
253
- inputRef.current.focus();
254
- ```
255
-
256
- ### useReducer
257
-
258
- ```js
259
- const reducer = (state, action) => {
260
- switch (action.type) {
261
- case 'increment': return { count: state.count + 1 };
262
- case 'decrement': return { count: state.count - 1 };
263
- case 'reset': return { count: 0 };
264
- default: return state;
265
- }
266
- };
267
-
268
- const [state, dispatch] = useReducer(reducer, { count: 0 });
269
-
270
- dispatch({ type: 'increment' });
271
- ```
272
-
273
- ### useContext
274
-
275
- ```js
276
- const ThemeContext = createContext('light');
277
-
278
- // Provide value (in layout or parent)
279
- <ThemeContext.Provider value="dark">
280
- {children}
281
- </ThemeContext.Provider>
282
-
283
- // Consume anywhere
284
- const theme = useContext(ThemeContext);
285
- ```
286
-
287
- ### useLocalStorage
288
-
289
- ```js
290
- const [name, setName] = useLocalStorage('userName', '');
291
- ```
292
-
293
- ### useFetch
294
-
295
- ```js
296
- const { data, loading, error } = useFetch('/api/users');
297
-
298
- if (loading) return <p>Loading...</p>;
299
- if (error) return <p>Error: {error}</p>;
300
-
301
- return <pre>{JSON.stringify(data, null, 2)}</pre>;
302
- ```
303
-
304
- ---
305
-
306
- ## API Routes
307
-
308
- API routes run on the **server** (Node.js). Every `.js` file in `api/` becomes an endpoint.
309
-
310
- ### Basic example
311
-
312
- ```js
313
- // api/hello.js → GET /api/hello
314
- const { NextResponse } = require('../_framework/next-response');
315
-
316
- async function GET(request) {
317
- const { searchParams } = new URL(request.url);
318
- const name = searchParams.get('name') || 'World';
319
- return NextResponse.json({ message: `Hello, ${name}!` });
320
- }
321
-
322
- module.exports = { GET };
323
- ```
324
-
325
- ### CRUD example
326
-
327
- ```js
328
- // api/users.js → /api/users
329
- const { NextResponse } = require('../_framework/next-response');
330
-
331
- async function GET(request) {
332
- const users = await User.find();
333
- return NextResponse.json(users);
334
- }
335
-
336
- async function POST(request) {
337
- const { name, email } = await request.json();
338
- if (!name || !email) {
339
- return NextResponse.json({ error: 'Name and email required' }, { status: 400 });
340
- }
341
- const user = await User.create({ name, email });
342
- return NextResponse.json(user, { status: 201 });
343
- }
344
-
345
- async function DELETE(request) {
346
- const { searchParams } = new URL(request.url);
347
- await User.findByIdAndDelete(searchParams.get('id'));
348
- return NextResponse.json({ message: 'Deleted' });
349
- }
350
-
351
- module.exports = { GET, POST, DELETE };
352
- ```
353
-
354
- ### NextResponse
355
-
356
- ```js
357
- return NextResponse.json({ data: 'value' });
358
- return NextResponse.json({ error: 'Not found' }, { status: 404 });
359
- return NextResponse.redirect('/login');
360
- return NextResponse.text('Hello');
361
- ```
362
-
363
- ---
364
-
365
- ## Imports
366
-
367
- ### Components and pages — no imports needed for JSX or hooks
368
-
369
- ```js
370
- // app/dashboard/page.js
371
- export default function Page() {
372
- const [count, setCount] = useState(0); // hooks are global
373
- return <h1>Count: {count}</h1>; // JSX works automatically
374
- }
375
- ```
376
-
377
- ### Importing other files
378
-
379
- ```js
380
- // Absolute path
381
- import Navbar from '/components/Navbar.js';
382
- import connectDB from '/lib/db.js';
383
- import User from '/models/User.js';
384
-
385
- // @/ alias (same as absolute)
386
- import connectDB from '@/lib/db';
387
- import User from '@/models/User';
388
-
389
- // npm packages (install first: npm install mongoose)
390
- import mongoose from 'mongoose';
391
- import bcrypt from 'bcryptjs';
392
- import jwt from 'jsonwebtoken';
393
- ```
394
-
395
- The dev server resolves bare imports from `node_modules` automatically — no bundler config needed.
396
-
397
- ---
398
-
399
- ## Components
400
-
401
- Components are just functions that return JSX — no pragma or imports needed.
402
-
403
- ```js
404
- // components/Button.js
405
- export default function Button({ onClick, children, variant = 'primary' }) {
406
- return (
407
- <button class={`btn btn-${variant}`} onClick={onClick}>
408
- {children}
409
- </button>
410
- );
411
- }
412
- ```
413
-
414
- ```js
415
- // Using the component
416
- import Button from '/components/Button.js';
417
-
418
- <Button onClick={() => alert('hi')} variant="danger">Delete</Button>
419
- ```
420
-
421
- ### JSX rules
422
-
423
- ```js
424
- // Use class instead of className
425
- <div class="container">
426
-
427
- // Use onInput for text inputs (not onChange)
428
- <input onInput={(e) => setValue(e.target.value)} />
429
-
430
- // Self-closing tags need /
431
- <input type="text" />
432
-
433
- // Expressions in {}
434
- <p>{count > 0 ? 'positive' : 'zero'}</p>
435
-
436
- // Lists
437
- {items.map(item => <li key={item.id}>{item.name}</li>)}
438
-
439
- // Conditional rendering
440
- {isLoggedIn && <Dashboard />}
441
- ```
442
-
443
- ---
444
-
445
- ## Database
446
-
447
- ### MongoDB with Mongoose
448
-
449
- ```bash
450
- npm install mongoose
451
- ```
452
-
453
- ```js
454
- // lib/db.js
455
- import mongoose from 'mongoose';
456
-
457
- let isConnected = false;
458
-
459
- export default async function connectDB() {
460
- if (isConnected) return;
461
- await mongoose.connect(process.env.MONGODB_URI);
462
- isConnected = true;
463
- }
464
- ```
465
-
466
- ```js
467
- // models/User.js
468
- import mongoose from 'mongoose';
469
-
470
- const userSchema = new mongoose.Schema({
471
- name: { type: String, required: true },
472
- email: { type: String, required: true, unique: true },
473
- password: { type: String, required: true },
474
- }, { timestamps: true });
475
-
476
- export default mongoose.models.User || mongoose.model('User', userSchema);
477
- ```
478
-
479
- ### Environment variables
480
-
481
- Create `.env` in your project root:
482
-
483
- ```bash
484
- MONGODB_URI=mongodb://localhost:27017/myapp
485
- JWT_SECRET=your_secret_key
486
- ```
487
-
488
- > Install `dotenv` and add `require('dotenv').config()` at the top of `scripts/dev-server.js` to load `.env` automatically.
489
-
490
- ---
491
-
492
- ## Calling APIs from Pages
493
-
494
- ```js
495
- // GET
496
- const res = await fetch('/api/users');
497
- const users = await res.json();
498
-
499
- // POST
500
- const res = await fetch('/api/users', {
501
- method: 'POST',
502
- headers: { 'Content-Type': 'application/json' },
503
- body: JSON.stringify({ name: 'Aziz', email: 'aziz@example.com' })
504
- });
505
- const result = await res.json();
506
- ```
507
-
508
- ### Full login example
509
-
510
- ```js
511
- export default function Page() {
512
- const [email, setEmail] = useState('');
513
- const [password, setPassword] = useState('');
514
- const [error, setError] = useState('');
515
- const [loading, setLoading] = useState(false);
516
-
517
- const handleLogin = async (e) => {
518
- e.preventDefault();
519
- setLoading(true);
520
- setError('');
521
-
522
- try {
523
- const res = await fetch('/api/loginUser', {
524
- method: 'POST',
525
- headers: { 'Content-Type': 'application/json' },
526
- body: JSON.stringify({ email, password })
527
- });
528
-
529
- const result = await res.json();
530
-
531
- if (!res.ok) {
532
- setError(result.error);
533
- return;
534
- }
535
-
536
- localStorage.setItem('user', JSON.stringify(result));
537
- navigate(result.role === 'admin' ? '/admin' : '/dashboard');
538
-
539
- } catch (err) {
540
- setError('Something went wrong. Try again.');
541
- } finally {
542
- setLoading(false);
543
- }
544
- };
545
-
546
- return (
547
- <div style={{ maxWidth: '400px', margin: '80px auto', padding: '0 16px' }}>
548
- <h1>Login</h1>
549
- <form onSubmit={handleLogin}>
550
- <input
551
- type="email"
552
- placeholder="Email"
553
- value={email}
554
- onInput={(e) => setEmail(e.target.value)}
555
- required
556
- />
557
- <input
558
- type="password"
559
- placeholder="Password"
560
- value={password}
561
- onInput={(e) => setPassword(e.target.value)}
562
- required
563
- />
564
- {error && <p style={{ color: 'red' }}>{error}</p>}
565
- <button type="submit" class="btn btn-primary" disabled={loading}>
566
- {loading ? 'Logging in...' : 'Login'}
567
- </button>
568
- </form>
569
- </div>
570
- );
571
- }
572
- ```
573
-
574
- ---
575
-
576
- ## Styling
577
-
578
- ### globals.css
579
-
580
- Edit `styles/globals.css` for global styles. Built-in utility classes:
581
-
582
- ```css
583
- .card { background: white; border-radius: 10px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin-bottom: 20px; }
584
- .btn { display: inline-block; padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer; }
585
- .btn-primary { background: #61dafb; color: #282c34; font-weight: bold; }
586
- .btn-danger { background: #ff6b6b; color: white; }
587
- .btn-grey { background: #ccc; color: #333; }
588
- ```
589
-
590
- ### Inline styles
591
-
592
- ```js
593
- <div style={{ padding: '20px', color: 'red', fontSize: '16px' }}>
594
- Hello
595
- </div>
596
- ```
597
-
598
- ### Tailwind CSS
599
-
600
- Tailwind CSS is included by default via CDN with `primary` and `dark` colors pre-configured:
601
-
602
- ```js
603
- // Use Tailwind classes directly in any file
604
- <div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow">
605
- <h1 class="text-2xl font-bold text-dark">Hello</h1>
606
- <button class="bg-primary text-dark font-bold px-4 py-2 rounded-lg">
607
- Click me
608
- </button>
609
- </div>
610
- ```
611
-
612
- Custom colors available out of the box:
613
-
614
- | Class | Value |
615
- |-------|-------|
616
- | `bg-primary` / `text-primary` | `#61dafb` (cyan) |
617
- | `bg-dark` / `text-dark` | `#282c34` (dark) |
618
-
619
- To extend the config, edit the `tailwind.config` block in `index.html`.
620
-
621
- ---
622
-
623
- ## Dev Server
624
-
625
- ```bash
626
- npm run dev
627
- ```
628
-
629
- - Serves your app at `http://localhost:3000`
630
- - Watches all source folders and live-reloads on save
631
- - Resolves bare imports from `node_modules`
632
- - Hot-reloads API routes when changed
633
-
634
- ```bash
635
- # Custom port
636
- PORT=4000 npm run dev
637
- ```
638
-
639
- ---
640
-
641
- ## Comparison with Next.js
642
-
643
- | Feature | Aziz.js | Next.js |
644
- |---------|---------|---------|
645
- | File-based routing | ✅ | ✅ |
646
- | `page.js` convention | ✅ | ✅ |
647
- | `layout.js` convention | ✅ | ✅ |
648
- | Dynamic routes `[slug]` | ✅ | ✅ |
649
- | Catch-all `[...slug]` | ✅ | ✅ |
650
- | API routes | ✅ | ✅ |
651
- | `NextResponse` | ✅ | ✅ |
652
- | React hooks | ✅ | ✅ |
653
- | No imports needed in pages | ✅ | ❌ |
654
- | ES module imports | ✅ | ✅ |
655
- | `@/` path alias | ✅ | ✅ |
656
- | Live reload | ✅ | ✅ |
657
- | SSR / SSG | ❌ | ✅ |
658
- | TypeScript | ❌ | ✅ |
659
- | Bundle size | **~50KB** | ~400KB+ |
660
- | Dependencies | **0** | 20+ |
661
- | Startup time | **< 1s** | 10–30s |
662
-
663
- ---
664
-
665
- ## License
666
-
667
- MIT
1
+ # ⚡ Aziz.js
2
+
3
+ A lightweight Next.js clone with file-based routing, React hooks, and ES module imports — **zero dependencies, instant startup**.
4
+
5
+ ```
6
+ Next.js: ~400KB + 20 dependencies + 10s startup
7
+ Aziz.js: ~50KB + 0 dependencies + <1s startup
8
+ ```
9
+
10
+ ---
11
+
12
+ ## Quick Start
13
+
14
+ ```bash
15
+ npm install
16
+ npm run dev
17
+ ```
18
+
19
+ Open [http://localhost:3000](http://localhost:3000) — that's it.
20
+
21
+ ---
22
+
23
+ ## Project Structure
24
+
25
+ ```
26
+ my-app/
27
+ ├── index.html ← entry point (don't touch)
28
+ │
29
+ ├── app/ ← your pages and layouts
30
+ │ ├── layout.js ← root layout (wraps every page)
31
+ │ └── page.js ← route: /
32
+ │
33
+ ├── styles/
34
+ │ └── globals.css ← global styles
35
+ │
36
+ ├── _framework/ ← runtime, hooks, router (don't touch)
37
+ ├── scripts/ ← dev server + live reload (don't touch)
38
+ └── docs/ ← full documentation
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Pages
44
+
45
+ Every `page.js` inside `app/` becomes a route. Export a default function — no imports needed.
46
+
47
+ ```js
48
+ // app/about/page.js → route: /about
49
+ export default function Page() {
50
+ return (
51
+ <div>
52
+ <h1>About</h1>
53
+ <p>Hello from the about page!</p>
54
+ </div>
55
+ );
56
+ }
57
+ ```
58
+
59
+ ### Route mapping
60
+
61
+ | File | Route |
62
+ |------|-------|
63
+ | `app/page.js` | `/` |
64
+ | `app/about/page.js` | `/about` |
65
+ | `app/blog/page.js` | `/blog` |
66
+ | `app/blog/[slug]/page.js` | `/blog/:slug` |
67
+ | `app/blog/[...slug]/page.js` | `/blog/*` (catch-all) |
68
+
69
+ ### Creating a new page
70
+
71
+ Just create the file — no registration needed:
72
+
73
+ ```bash
74
+ mkdir app/dashboard
75
+ ```
76
+
77
+ ```js
78
+ // app/dashboard/page.js → route: /dashboard
79
+ export default function Page() {
80
+ return <h1>Dashboard</h1>;
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Layouts
87
+
88
+ `layout.js` wraps all pages at the same level and below. The root `app/layout.js` wraps every page.
89
+
90
+ ```js
91
+ // app/layout.js
92
+ export default function Layout({ children }) {
93
+ return (
94
+ <div>
95
+ <main>{children}</main>
96
+ </div>
97
+ );
98
+ }
99
+ ```
100
+
101
+ ### Nested layouts
102
+
103
+ ```js
104
+ // app/dashboard/layout.js — wraps all /dashboard/* pages
105
+ export default function Layout({ children }) {
106
+ return (
107
+ <div style={{ display: 'flex' }}>
108
+ <div style={{ flex: 1 }}>{children}</div>
109
+ </div>
110
+ );
111
+ }
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Navigation
117
+
118
+ ### navigate()
119
+
120
+ ```js
121
+ navigate('/about');
122
+ navigate('/blog/my-post');
123
+ navigate('/');
124
+ ```
125
+
126
+ ### Link component
127
+
128
+ ```js
129
+ <Link href="/about">About</Link>
130
+ <Link href="/blog/hello" className="nav-link">Blog Post</Link>
131
+ ```
132
+
133
+ ### useRouter hook
134
+
135
+ ```js
136
+ export default function Page() {
137
+ const router = useRouter();
138
+
139
+ return (
140
+ <div>
141
+ <p>Current path: {router.pathname}</p>
142
+ <button onClick={() => router.push('/about')}>Go to About</button>
143
+ <button onClick={() => router.back()}>Back</button>
144
+ </div>
145
+ );
146
+ }
147
+ ```
148
+
149
+ ### useParams — dynamic routes
150
+
151
+ ```js
152
+ // app/blog/[slug]/page.js
153
+ export default function Page() {
154
+ const { slug } = useParams();
155
+ return <h1>Post: {slug}</h1>;
156
+ }
157
+ ```
158
+
159
+ ### useSearchParams — query strings
160
+
161
+ ```js
162
+ // URL: /search?q=hello&page=2
163
+ export default function Page() {
164
+ const searchParams = useSearchParams();
165
+ const query = searchParams.get('q'); // "hello"
166
+ const page = searchParams.get('page'); // "2"
167
+
168
+ return <p>Searching for: {query}</p>;
169
+ }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## React Hooks
175
+
176
+ All hooks work exactly like React — **no imports needed**.
177
+
178
+ ### useState
179
+
180
+ ```js
181
+ const [count, setCount] = useState(0);
182
+
183
+ setCount(prev => prev + 1);
184
+ ```
185
+
186
+ ### useEffect
187
+
188
+ ```js
189
+ useEffect(() => {
190
+ document.title = `Count: ${count}`;
191
+ }, [count]);
192
+
193
+ useEffect(() => {
194
+ const timer = setInterval(() => tick(), 1000);
195
+ return () => clearInterval(timer);
196
+ }, []);
197
+ ```
198
+
199
+ ### useMemo
200
+
201
+ ```js
202
+ const total = useMemo(() => {
203
+ return items.filter(i => i.active).length;
204
+ }, [items]);
205
+ ```
206
+
207
+ ### useCallback
208
+
209
+ ```js
210
+ const handleClick = useCallback(() => {
211
+ doSomething(id);
212
+ }, [id]);
213
+ ```
214
+
215
+ ### useRef
216
+
217
+ ```js
218
+ const inputRef = useRef(null);
219
+
220
+ <input ref={inputRef} type="text" />
221
+
222
+ inputRef.current.focus();
223
+ ```
224
+
225
+ ### useReducer
226
+
227
+ ```js
228
+ const reducer = (state, action) => {
229
+ switch (action.type) {
230
+ case 'increment': return { count: state.count + 1 };
231
+ case 'decrement': return { count: state.count - 1 };
232
+ default: return state;
233
+ }
234
+ };
235
+
236
+ const [state, dispatch] = useReducer(reducer, { count: 0 });
237
+ ```
238
+
239
+ ### useContext
240
+
241
+ ```js
242
+ const ThemeContext = createContext('light');
243
+
244
+ <ThemeContext.Provider value="dark">
245
+ {children}
246
+ </ThemeContext.Provider>
247
+
248
+ const theme = useContext(ThemeContext);
249
+ ```
250
+
251
+ ### useLocalStorage
252
+
253
+ ```js
254
+ const [name, setName] = useLocalStorage('userName', '');
255
+ ```
256
+
257
+ ---
258
+
259
+ ## Imports
260
+
261
+ ### Importing other files
262
+
263
+ ```js
264
+ // Absolute path
265
+ import Button from '/components/Button.js';
266
+
267
+ // @/ alias (same as absolute)
268
+ import Button from '@/components/Button';
269
+
270
+ // npm packages (install first: npm install lodash)
271
+ import _ from 'lodash';
272
+ ```
273
+
274
+ The dev server resolves bare imports from `node_modules` automatically — no bundler config needed.
275
+
276
+ ---
277
+
278
+ ## Components
279
+
280
+ Components are just functions that return JSX — no pragma or imports needed.
281
+
282
+ ```js
283
+ // components/Button.js
284
+ export default function Button({ onClick, children, variant = 'primary' }) {
285
+ return (
286
+ <button class={`btn btn-${variant}`} onClick={onClick}>
287
+ {children}
288
+ </button>
289
+ );
290
+ }
291
+ ```
292
+
293
+ ```js
294
+ // Using the component
295
+ import Button from '/components/Button.js';
296
+
297
+ <Button onClick={() => alert('hi')} variant="danger">Delete</Button>
298
+ ```
299
+
300
+ ### JSX rules
301
+
302
+ ```js
303
+ // Use class instead of className
304
+ <div class="container">
305
+
306
+ // Use onInput for text inputs (not onChange)
307
+ <input onInput={(e) => setValue(e.target.value)} />
308
+
309
+ // Self-closing tags need /
310
+ <input type="text" />
311
+
312
+ // Expressions in {}
313
+ <p>{count > 0 ? 'positive' : 'zero'}</p>
314
+
315
+ // Lists
316
+ {items.map(item => <li key={item.id}>{item.name}</li>)}
317
+
318
+ // Conditional rendering
319
+ {isLoggedIn && <Dashboard />}
320
+ ```
321
+
322
+ ---
323
+
324
+ ## Styling
325
+
326
+ ### globals.css
327
+
328
+ Edit `styles/globals.css` for global styles. Built-in utility classes:
329
+
330
+ ```css
331
+ .card { background: white; border-radius: 10px; padding: 24px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); margin-bottom: 20px; }
332
+ .btn { display: inline-block; padding: 10px 22px; border: none; border-radius: 8px; cursor: pointer; }
333
+ .btn-primary { background: #61dafb; color: #282c34; font-weight: bold; }
334
+ .btn-danger { background: #ff6b6b; color: white; }
335
+ .btn-grey { background: #ccc; color: #333; }
336
+ ```
337
+
338
+ ### Inline styles
339
+
340
+ ```js
341
+ <div style={{ padding: '20px', color: 'red', fontSize: '16px' }}>
342
+ Hello
343
+ </div>
344
+ ```
345
+
346
+ ### Tailwind CSS
347
+
348
+ If you scaffolded with Tailwind, it is loaded via CDN with `primary` and `dark` colors pre-configured:
349
+
350
+ ```js
351
+ // Use Tailwind classes directly in any file
352
+ <div class="flex items-center gap-4 p-6 bg-white rounded-lg shadow">
353
+ <h1 class="text-2xl font-bold text-dark">Hello</h1>
354
+ </div>
355
+ ```
356
+
357
+ To extend the config, edit the `tailwind.config` block in `index.html`.
358
+
359
+ ---
360
+
361
+ ## Dev Server
362
+
363
+ ```bash
364
+ npm run dev
365
+ ```
366
+
367
+ - Serves your app at `http://localhost:3000`
368
+ - Watches all source folders and live-reloads on save
369
+ - Resolves bare imports from `node_modules`
370
+
371
+ ```bash
372
+ # Custom port
373
+ PORT=4000 npm run dev
374
+ ```
375
+
376
+ ---
377
+
378
+ ## Comparison with Next.js
379
+
380
+ | Feature | Aziz.js | Next.js |
381
+ |---------|---------|---------|
382
+ | File-based routing | ✅ | ✅ |
383
+ | `page.js` convention | ✅ | ✅ |
384
+ | `layout.js` convention | ✅ | ✅ |
385
+ | Dynamic routes `[slug]` | ✅ | ✅ |
386
+ | Catch-all `[...slug]` | ✅ | ✅ |
387
+ | React hooks | ✅ | ✅ |
388
+ | No imports needed in pages | ✅ | ❌ |
389
+ | ES module imports | ✅ | ✅ |
390
+ | `@/` path alias | ✅ | ✅ |
391
+ | Live reload | ✅ | ✅ |
392
+ | SSR / SSG | ❌ | ✅ |
393
+ | TypeScript | ❌ | ✅ |
394
+ | Bundle size | **~50KB** | ~400KB+ |
395
+ | Dependencies | **0** | 20+ |
396
+ | Startup time | **< 1s** | 10–30s |
397
+
398
+ ---
399
+
400
+ ## License
401
+
402
+ MIT