@jgamaraalv/ts-dev-kit 1.0.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 (117) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +24 -0
  3. package/CHANGELOG.md +24 -0
  4. package/LICENSE +21 -0
  5. package/README.md +128 -0
  6. package/agents/accessibility-pro.md +139 -0
  7. package/agents/api-builder.md +110 -0
  8. package/agents/code-reviewer.md +190 -0
  9. package/agents/database-expert.md +138 -0
  10. package/agents/debugger.md +241 -0
  11. package/agents/docker-expert.md +51 -0
  12. package/agents/multi-agent-coordinator.md +378 -0
  13. package/agents/nextjs-expert.md +136 -0
  14. package/agents/performance-engineer.md +138 -0
  15. package/agents/playwright-expert.md +126 -0
  16. package/agents/react-specialist.md +97 -0
  17. package/agents/security-scanner.md +105 -0
  18. package/agents/test-generator.md +221 -0
  19. package/agents/typescript-pro.md +253 -0
  20. package/agents/ux-optimizer.md +93 -0
  21. package/docs/rules/orchestration.md.template +126 -0
  22. package/package.json +28 -0
  23. package/skills/bullmq/SKILL.md +225 -0
  24. package/skills/bullmq/references/flows-and-schedulers.md +186 -0
  25. package/skills/bullmq/references/job-types-and-options.md +163 -0
  26. package/skills/bullmq/references/patterns.md +273 -0
  27. package/skills/bullmq/references/production.md +308 -0
  28. package/skills/composition-patterns/SKILL.md +58 -0
  29. package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
  30. package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
  31. package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
  32. package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
  33. package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
  34. package/skills/composition-patterns/references/state-context-interface.md +194 -0
  35. package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
  36. package/skills/composition-patterns/references/state-lift-state.md +126 -0
  37. package/skills/conventional-commits/SKILL.md +148 -0
  38. package/skills/docker/SKILL.md +55 -0
  39. package/skills/docker/references/compose-configs.md +95 -0
  40. package/skills/docker/references/monorepo-dockerfile.md +111 -0
  41. package/skills/drizzle-pg/SKILL.md +202 -0
  42. package/skills/drizzle-pg/references/advanced.md +299 -0
  43. package/skills/drizzle-pg/references/migrations.md +214 -0
  44. package/skills/drizzle-pg/references/queries.md +321 -0
  45. package/skills/drizzle-pg/references/relations.md +272 -0
  46. package/skills/drizzle-pg/references/schema-pg.md +256 -0
  47. package/skills/drizzle-pg/references/sql-operator.md +215 -0
  48. package/skills/fastify-best-practices/SKILL.md +143 -0
  49. package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
  50. package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
  51. package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
  52. package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
  53. package/skills/fastify-best-practices/references/server-and-options.md +127 -0
  54. package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
  55. package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
  56. package/skills/ioredis/SKILL.md +51 -0
  57. package/skills/ioredis/references/advanced-patterns.md +312 -0
  58. package/skills/ioredis/references/cluster-sentinel.md +280 -0
  59. package/skills/ioredis/references/connection-options.md +187 -0
  60. package/skills/ioredis/references/core-api.md +179 -0
  61. package/skills/nextjs-best-practices/SKILL.md +194 -0
  62. package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
  63. package/skills/nextjs-best-practices/references/bundling.md +192 -0
  64. package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
  65. package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
  66. package/skills/nextjs-best-practices/references/directives.md +74 -0
  67. package/skills/nextjs-best-practices/references/error-handling.md +237 -0
  68. package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
  69. package/skills/nextjs-best-practices/references/font.md +175 -0
  70. package/skills/nextjs-best-practices/references/functions.md +116 -0
  71. package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
  72. package/skills/nextjs-best-practices/references/image.md +184 -0
  73. package/skills/nextjs-best-practices/references/metadata.md +305 -0
  74. package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
  75. package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
  76. package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
  77. package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
  78. package/skills/nextjs-best-practices/references/scripts.md +148 -0
  79. package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
  80. package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
  81. package/skills/owasp-security-review/SKILL.md +98 -0
  82. package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
  83. package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
  84. package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
  85. package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
  86. package/skills/owasp-security-review/references/a05-injection.md +106 -0
  87. package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
  88. package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
  89. package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
  90. package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
  91. package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
  92. package/skills/postgresql/SKILL.md +50 -0
  93. package/skills/postgresql/references/ddl-schema.md +300 -0
  94. package/skills/postgresql/references/indexes.md +257 -0
  95. package/skills/postgresql/references/jsonb.md +261 -0
  96. package/skills/postgresql/references/performance.md +291 -0
  97. package/skills/postgresql/references/psql-cli.md +153 -0
  98. package/skills/postgresql/references/queries.md +287 -0
  99. package/skills/postgresql/references/transactions.md +280 -0
  100. package/skills/react-best-practices/SKILL.md +110 -0
  101. package/skills/react-best-practices/references/advanced-patterns.md +91 -0
  102. package/skills/react-best-practices/references/async-patterns.md +233 -0
  103. package/skills/react-best-practices/references/bundle-optimization.md +201 -0
  104. package/skills/react-best-practices/references/client-patterns.md +178 -0
  105. package/skills/react-best-practices/references/js-performance.md +210 -0
  106. package/skills/react-best-practices/references/rendering-performance.md +209 -0
  107. package/skills/react-best-practices/references/rerender-optimization.md +316 -0
  108. package/skills/react-best-practices/references/server-performance.md +274 -0
  109. package/skills/service-worker/SKILL.md +195 -0
  110. package/skills/service-worker/references/api-reference.md +114 -0
  111. package/skills/service-worker/references/caching-strategies.md +202 -0
  112. package/skills/service-worker/references/push-and-sync.md +261 -0
  113. package/skills/typescript-conventions/SKILL.md +51 -0
  114. package/skills/ui-ux-guidelines/SKILL.md +105 -0
  115. package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
  116. package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
  117. package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
@@ -0,0 +1,86 @@
1
+ # Hydration Errors
2
+
3
+ Diagnose and fix React hydration mismatch errors.
4
+
5
+ ## Error Signs
6
+
7
+ - "Hydration failed because the initial UI does not match"
8
+ - "Text content does not match server-rendered HTML"
9
+
10
+ ## Debugging
11
+
12
+ In development, click the hydration error to see the server/client diff.
13
+
14
+ ## Common Causes and Fixes
15
+
16
+ ### Browser-only APIs
17
+
18
+ ```tsx
19
+ // Bad: Causes mismatch - window doesn't exist on server
20
+ <div>{window.innerWidth}</div>;
21
+
22
+ // Good: Use client component with mounted check
23
+ ("use client");
24
+ import { useState, useEffect } from "react";
25
+
26
+ export function ClientOnly({ children }: { children: React.ReactNode }) {
27
+ const [mounted, setMounted] = useState(false);
28
+ useEffect(() => setMounted(true), []);
29
+ return mounted ? children : null;
30
+ }
31
+ ```
32
+
33
+ ### Date/Time Rendering
34
+
35
+ Server and client may be in different timezones:
36
+
37
+ ```tsx
38
+ // Bad: Causes mismatch
39
+ <span>{new Date().toLocaleString()}</span>;
40
+
41
+ // Good: Render on client only
42
+ ("use client");
43
+ const [time, setTime] = useState<string>();
44
+ useEffect(() => setTime(new Date().toLocaleString()), []);
45
+ ```
46
+
47
+ ### Random Values or IDs
48
+
49
+ ```tsx
50
+ // Bad: Random values differ between server and client
51
+ <div id={Math.random().toString()}>
52
+
53
+ // Good: Use useId hook
54
+ import { useId } from 'react'
55
+
56
+ function Input() {
57
+ const id = useId()
58
+ return <input id={id} />
59
+ }
60
+ ```
61
+
62
+ ### Invalid HTML Nesting
63
+
64
+ ```tsx
65
+ // Bad: Invalid - div inside p
66
+ <p><div>Content</div></p>
67
+
68
+ // Bad: Invalid - p inside p
69
+ <p><p>Nested</p></p>
70
+
71
+ // Good: Valid nesting
72
+ <div><p>Content</p></div>
73
+ ```
74
+
75
+ ### Third-party Scripts
76
+
77
+ Scripts that modify DOM during hydration.
78
+
79
+ ```tsx
80
+ // Good: Use next/script with afterInteractive
81
+ import Script from "next/script";
82
+
83
+ export default function Page() {
84
+ return <Script src="https://example.com/script.js" strategy="afterInteractive" />;
85
+ }
86
+ ```
@@ -0,0 +1,184 @@
1
+ # Image Optimization
2
+
3
+ Use `next/image` for automatic image optimization.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Always Use next/image](#always-use-nextimage)
8
+ - [Required Props](#required-props)
9
+ - [Remote Images Configuration](#remote-images-configuration)
10
+ - [Responsive Images](#responsive-images)
11
+ - [Blur Placeholder](#blur-placeholder)
12
+ - [Priority Loading](#priority-loading)
13
+ - [Common Mistakes](#common-mistakes)
14
+ - [Static Export](#static-export)
15
+
16
+ ## Always Use next/image
17
+
18
+ ```tsx
19
+ // Bad: Avoid native img
20
+ <img src="/hero.png" alt="Hero" />;
21
+
22
+ // Good: Use next/image
23
+ import Image from "next/image";
24
+ <Image src="/hero.png" alt="Hero" width={800} height={400} />;
25
+ ```
26
+
27
+ ## Required Props
28
+
29
+ Images need explicit dimensions to prevent layout shift:
30
+
31
+ ```tsx
32
+ // Local images - dimensions inferred automatically
33
+ import heroImage from './hero.png'
34
+ <Image src={heroImage} alt="Hero" />
35
+
36
+ // Remote images - must specify width/height
37
+ <Image src="https://example.com/image.jpg" alt="Hero" width={800} height={400} />
38
+
39
+ // Or use fill for parent-relative sizing
40
+ <div style={{ position: 'relative', width: '100%', height: 400 }}>
41
+ <Image src="/hero.png" alt="Hero" fill style={{ objectFit: 'cover' }} />
42
+ </div>
43
+ ```
44
+
45
+ ## Remote Images Configuration
46
+
47
+ Remote domains must be configured in `next.config.js`:
48
+
49
+ ```js
50
+ // next.config.js
51
+ export default {
52
+ images: {
53
+ remotePatterns: [
54
+ {
55
+ protocol: "https",
56
+ hostname: "example.com",
57
+ pathname: "/images/**",
58
+ },
59
+ {
60
+ protocol: "https",
61
+ hostname: "*.cdn.com", // Wildcard subdomain
62
+ },
63
+ ],
64
+ },
65
+ };
66
+ ```
67
+
68
+ ## Responsive Images
69
+
70
+ Use `sizes` to tell the browser which size to download:
71
+
72
+ ```tsx
73
+ // Full-width hero
74
+ <Image
75
+ src="/hero.png"
76
+ alt="Hero"
77
+ fill
78
+ sizes="100vw"
79
+ />
80
+
81
+ // Responsive grid (3 columns on desktop, 1 on mobile)
82
+ <Image
83
+ src="/card.png"
84
+ alt="Card"
85
+ fill
86
+ sizes="(max-width: 768px) 100vw, 33vw"
87
+ />
88
+
89
+ // Fixed sidebar image
90
+ <Image
91
+ src="/avatar.png"
92
+ alt="Avatar"
93
+ width={200}
94
+ height={200}
95
+ sizes="200px"
96
+ />
97
+ ```
98
+
99
+ ## Blur Placeholder
100
+
101
+ Prevent layout shift with placeholders:
102
+
103
+ ```tsx
104
+ // Local images - automatic blur hash
105
+ import heroImage from './hero.png'
106
+ <Image src={heroImage} alt="Hero" placeholder="blur" />
107
+
108
+ // Remote images - provide blurDataURL
109
+ <Image
110
+ src="https://example.com/image.jpg"
111
+ alt="Hero"
112
+ width={800}
113
+ height={400}
114
+ placeholder="blur"
115
+ blurDataURL="data:image/jpeg;base64,/9j/4AAQSkZJRg..."
116
+ />
117
+
118
+ // Or use color placeholder
119
+ <Image
120
+ src="https://example.com/image.jpg"
121
+ alt="Hero"
122
+ width={800}
123
+ height={400}
124
+ placeholder="empty"
125
+ style={{ backgroundColor: '#e0e0e0' }}
126
+ />
127
+ ```
128
+
129
+ ## Priority Loading
130
+
131
+ Use `priority` for above-the-fold images (LCP):
132
+
133
+ ```tsx
134
+ // Hero image - loads immediately
135
+ <Image src="/hero.png" alt="Hero" fill priority />
136
+
137
+ // Below-fold images - lazy loaded by default (no priority needed)
138
+ <Image src="/card.png" alt="Card" width={400} height={300} />
139
+ ```
140
+
141
+ ## Common Mistakes
142
+
143
+ ```tsx
144
+ // Bad: Missing sizes with fill - downloads largest image
145
+ <Image src="/hero.png" alt="Hero" fill />
146
+
147
+ // Good: Add sizes for proper responsive behavior
148
+ <Image src="/hero.png" alt="Hero" fill sizes="100vw" />
149
+
150
+ // Bad: Using width/height for aspect ratio only
151
+ <Image src="/hero.png" alt="Hero" width={16} height={9} />
152
+
153
+ // Good: Use actual display dimensions or fill with sizes
154
+ <Image src="/hero.png" alt="Hero" fill sizes="100vw" style={{ objectFit: 'cover' }} />
155
+
156
+ // Bad: Remote image without config
157
+ <Image src="https://untrusted.com/image.jpg" alt="Image" width={400} height={300} />
158
+ // Error: Invalid src prop, hostname not configured
159
+
160
+ // Good: Add hostname to next.config.js remotePatterns
161
+ ```
162
+
163
+ ## Static Export
164
+
165
+ When using `output: 'export'`, use `unoptimized` or custom loader:
166
+
167
+ ```tsx
168
+ // Option 1: Disable optimization
169
+ <Image src="/hero.png" alt="Hero" width={800} height={400} unoptimized />;
170
+
171
+ // Option 2: Global config
172
+ // next.config.js
173
+ export default {
174
+ output: "export",
175
+ images: { unoptimized: true },
176
+ };
177
+
178
+ // Option 3: Custom loader (Cloudinary, Imgix, etc.)
179
+ const cloudinaryLoader = ({ src, width, quality }) => {
180
+ return `https://res.cloudinary.com/demo/image/upload/w_${width},q_${quality || 75}/${src}`;
181
+ };
182
+
183
+ <Image loader={cloudinaryLoader} src="sample.jpg" alt="Sample" width={800} height={400} />;
184
+ ```
@@ -0,0 +1,305 @@
1
+ # Metadata
2
+
3
+ Add SEO metadata to Next.js pages using the Metadata API.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Important: Server Components Only](#important-server-components-only)
8
+ - [Static Metadata](#static-metadata)
9
+ - [Dynamic Metadata](#dynamic-metadata)
10
+ - [Avoid Duplicate Fetches](#avoid-duplicate-fetches)
11
+ - [Viewport](#viewport)
12
+ - [Title Templates](#title-templates)
13
+ - [Metadata File Conventions](#metadata-file-conventions)
14
+ - [SEO Best Practice: Static Files Are Often Enough](#seo-best-practice-static-files-are-often-enough)
15
+ - [OG Image Generation](#og-image-generation)
16
+ - [Multiple Sitemaps](#multiple-sitemaps)
17
+
18
+ ## Important: Server Components Only
19
+
20
+ The `metadata` object and `generateMetadata` function are **only supported in Server Components**. They cannot be used in Client Components.
21
+
22
+ If the target page has `'use client'`:
23
+
24
+ 1. Remove `'use client'` if possible, move client logic to child components
25
+ 2. Or extract metadata to a parent Server Component layout
26
+ 3. Or split the file: Server Component with metadata imports Client Components
27
+
28
+ ## Static Metadata
29
+
30
+ ```tsx
31
+ import type { Metadata } from "next";
32
+
33
+ export const metadata: Metadata = {
34
+ title: "Page Title",
35
+ description: "Page description for search engines",
36
+ };
37
+ ```
38
+
39
+ ## Dynamic Metadata
40
+
41
+ ```tsx
42
+ import type { Metadata } from "next";
43
+
44
+ type Props = { params: Promise<{ slug: string }> };
45
+
46
+ export async function generateMetadata({ params }: Props): Promise<Metadata> {
47
+ const { slug } = await params;
48
+ const post = await getPost(slug);
49
+ return { title: post.title, description: post.description };
50
+ }
51
+ ```
52
+
53
+ ## Avoid Duplicate Fetches
54
+
55
+ Use React `cache()` when the same data is needed for both metadata and page:
56
+
57
+ ```tsx
58
+ import { cache } from "react";
59
+
60
+ export const getPost = cache(async (slug: string) => {
61
+ return await db.posts.findFirst({ where: { slug } });
62
+ });
63
+ ```
64
+
65
+ ## Viewport
66
+
67
+ Separate from metadata for streaming support:
68
+
69
+ ```tsx
70
+ import type { Viewport } from "next";
71
+
72
+ export const viewport: Viewport = {
73
+ width: "device-width",
74
+ initialScale: 1,
75
+ themeColor: "#000000",
76
+ };
77
+
78
+ // Or dynamic
79
+ export function generateViewport({ params }): Viewport {
80
+ return { themeColor: getThemeColor(params) };
81
+ }
82
+ ```
83
+
84
+ ## Title Templates
85
+
86
+ In root layout for consistent naming:
87
+
88
+ ```tsx
89
+ export const metadata: Metadata = {
90
+ title: { default: "Site Name", template: "%s | Site Name" },
91
+ };
92
+ ```
93
+
94
+ ## Metadata File Conventions
95
+
96
+ Reference: https://nextjs.org/docs/app/getting-started/project-structure#metadata-file-conventions
97
+
98
+ Place these files in `app/` directory (or route segments):
99
+
100
+ | File | Purpose |
101
+ | ------------------------------- | --------------------------------------------- |
102
+ | `favicon.ico` | Favicon |
103
+ | `icon.png` / `icon.svg` | App icon |
104
+ | `apple-icon.png` | Apple app icon |
105
+ | `opengraph-image.png` | OG image |
106
+ | `twitter-image.png` | Twitter card image |
107
+ | `sitemap.ts` / `sitemap.xml` | Sitemap (use `generateSitemaps` for multiple) |
108
+ | `robots.ts` / `robots.txt` | Robots directives |
109
+ | `manifest.ts` / `manifest.json` | Web app manifest |
110
+
111
+ ## SEO Best Practice: Static Files Are Often Enough
112
+
113
+ For most sites, **static metadata files provide excellent SEO coverage**:
114
+
115
+ ```
116
+ app/
117
+ ├── favicon.ico
118
+ ├── opengraph-image.png # Works for both OG and Twitter
119
+ ├── sitemap.ts
120
+ ├── robots.ts
121
+ └── layout.tsx # With title/description metadata
122
+ ```
123
+
124
+ **Tips:**
125
+
126
+ - A single `opengraph-image.png` covers both Open Graph and Twitter (Twitter falls back to OG)
127
+ - Static `title` and `description` in layout metadata is sufficient for most pages
128
+ - Only use dynamic `generateMetadata` when content varies per page
129
+
130
+ ---
131
+
132
+ # OG Image Generation
133
+
134
+ Generate dynamic Open Graph images using `next/og`.
135
+
136
+ ## Important Rules
137
+
138
+ 1. **Use `next/og`** - not `@vercel/og` (it's built into Next.js)
139
+ 2. **No searchParams** - OG images can't access search params, use route params instead
140
+ 3. **Avoid Edge runtime** - Use default Node.js runtime
141
+
142
+ ```tsx
143
+ // Good
144
+ import { ImageResponse } from "next/og";
145
+
146
+ // Bad
147
+ // import { ImageResponse } from '@vercel/og'
148
+ // export const runtime = 'edge'
149
+ ```
150
+
151
+ ## Basic OG Image
152
+
153
+ ```tsx
154
+ // app/opengraph-image.tsx
155
+ import { ImageResponse } from "next/og";
156
+
157
+ export const alt = "Site Name";
158
+ export const size = { width: 1200, height: 630 };
159
+ export const contentType = "image/png";
160
+
161
+ export default function Image() {
162
+ return new ImageResponse(
163
+ <div
164
+ style={{
165
+ fontSize: 128,
166
+ background: "white",
167
+ width: "100%",
168
+ height: "100%",
169
+ display: "flex",
170
+ alignItems: "center",
171
+ justifyContent: "center",
172
+ }}
173
+ >
174
+ Hello World
175
+ </div>,
176
+ { ...size },
177
+ );
178
+ }
179
+ ```
180
+
181
+ ## Dynamic OG Image
182
+
183
+ ```tsx
184
+ // app/blog/[slug]/opengraph-image.tsx
185
+ import { ImageResponse } from "next/og";
186
+
187
+ export const alt = "Blog Post";
188
+ export const size = { width: 1200, height: 630 };
189
+ export const contentType = "image/png";
190
+
191
+ type Props = { params: Promise<{ slug: string }> };
192
+
193
+ export default async function Image({ params }: Props) {
194
+ const { slug } = await params;
195
+ const post = await getPost(slug);
196
+
197
+ return new ImageResponse(
198
+ <div
199
+ style={{
200
+ fontSize: 48,
201
+ background: "linear-gradient(to bottom, #1a1a1a, #333)",
202
+ color: "white",
203
+ width: "100%",
204
+ height: "100%",
205
+ display: "flex",
206
+ flexDirection: "column",
207
+ alignItems: "center",
208
+ justifyContent: "center",
209
+ padding: 48,
210
+ }}
211
+ >
212
+ <div style={{ fontSize: 64, fontWeight: "bold" }}>{post.title}</div>
213
+ <div style={{ marginTop: 24, opacity: 0.8 }}>{post.description}</div>
214
+ </div>,
215
+ { ...size },
216
+ );
217
+ }
218
+ ```
219
+
220
+ ## Custom Fonts
221
+
222
+ ```tsx
223
+ import { ImageResponse } from "next/og";
224
+ import { join } from "path";
225
+ import { readFile } from "fs/promises";
226
+
227
+ export default async function Image() {
228
+ const fontPath = join(process.cwd(), "assets/fonts/Inter-Bold.ttf");
229
+ const fontData = await readFile(fontPath);
230
+
231
+ return new ImageResponse(
232
+ <div style={{ fontFamily: "Inter", fontSize: 64 }}>Custom Font Text</div>,
233
+ {
234
+ width: 1200,
235
+ height: 630,
236
+ fonts: [{ name: "Inter", data: fontData, style: "normal" }],
237
+ },
238
+ );
239
+ }
240
+ ```
241
+
242
+ ## File Naming
243
+
244
+ - `opengraph-image.tsx` - Open Graph (Facebook, LinkedIn)
245
+ - `twitter-image.tsx` - Twitter/X cards (optional, falls back to OG)
246
+
247
+ ## Styling Notes
248
+
249
+ ImageResponse uses Flexbox layout:
250
+
251
+ - Use `display: 'flex'`
252
+ - No CSS Grid support
253
+ - Styles must be inline objects
254
+
255
+ ## Multiple OG Images
256
+
257
+ Use `generateImageMetadata` for multiple images per route:
258
+
259
+ ```tsx
260
+ // app/blog/[slug]/opengraph-image.tsx
261
+ import { ImageResponse } from "next/og";
262
+
263
+ export async function generateImageMetadata({ params }) {
264
+ const images = await getPostImages(params.slug);
265
+ return images.map((img, idx) => ({
266
+ id: idx,
267
+ alt: img.alt,
268
+ size: { width: 1200, height: 630 },
269
+ contentType: "image/png",
270
+ }));
271
+ }
272
+
273
+ export default async function Image({ params, id }) {
274
+ const images = await getPostImages(params.slug);
275
+ const image = images[id];
276
+ return new ImageResponse(/* ... */);
277
+ }
278
+ ```
279
+
280
+ ## Multiple Sitemaps
281
+
282
+ Use `generateSitemaps` for large sites:
283
+
284
+ ```tsx
285
+ // app/sitemap.ts
286
+ import type { MetadataRoute } from "next";
287
+
288
+ export async function generateSitemaps() {
289
+ // Return array of sitemap IDs
290
+ return [{ id: 0 }, { id: 1 }, { id: 2 }];
291
+ }
292
+
293
+ export default async function sitemap({ id }: { id: number }): Promise<MetadataRoute.Sitemap> {
294
+ const start = id * 50000;
295
+ const end = start + 50000;
296
+ const products = await getProducts(start, end);
297
+
298
+ return products.map((product) => ({
299
+ url: `https://example.com/product/${product.id}`,
300
+ lastModified: product.updatedAt,
301
+ }));
302
+ }
303
+ ```
304
+
305
+ Generates `/sitemap/0.xml`, `/sitemap/1.xml`, etc.