|
| 1 | +# PageMetadata Component - Usage Guide |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The `PageMetadata` component leverages React 19's native metadata support to render SEO tags directly in your components without needing external libraries like `react-helmet`. |
| 6 | + |
| 7 | +## Quick Start |
| 8 | + |
| 9 | +### Basic Usage |
| 10 | + |
| 11 | +```tsx |
| 12 | +import { PageMetadata } from "@components/common/PageMetadata"; |
| 13 | + |
| 14 | +export const MyPage = () => { |
| 15 | + return ( |
| 16 | + <> |
| 17 | + <PageMetadata |
| 18 | + title="My Page Title" |
| 19 | + description="A brief description of my page" |
| 20 | + /> |
| 21 | + |
| 22 | + {/* Your page content */} |
| 23 | + </> |
| 24 | + ); |
| 25 | +}; |
| 26 | +``` |
| 27 | + |
| 28 | +### Using Centralized Configuration |
| 29 | + |
| 30 | +For consistency across the site, use the centralized metadata configuration: |
| 31 | + |
| 32 | +```tsx |
| 33 | +import { PageMetadata } from "@components/common/PageMetadata"; |
| 34 | +import { getPageMetadata } from "@config/metadata"; |
| 35 | + |
| 36 | +export const HomeWrapper2025 = () => { |
| 37 | + return ( |
| 38 | + <> |
| 39 | + <PageMetadata {...getPageMetadata("home2025")} /> |
| 40 | + |
| 41 | + {/* Your page content */} |
| 42 | + </> |
| 43 | + ); |
| 44 | +}; |
| 45 | +``` |
| 46 | + |
| 47 | +## Advanced Usage |
| 48 | + |
| 49 | +### Custom Metadata |
| 50 | + |
| 51 | +```tsx |
| 52 | +<PageMetadata |
| 53 | + title="DevBcn 2025 - Speakers" |
| 54 | + description="Meet our amazing speakers for DevBcn 2025" |
| 55 | + canonicalUrl="https://www.devbcn.com/2025/speakers" |
| 56 | + ogImage="https://www.devbcn.com/images/speakers-2025.jpg" |
| 57 | + ogType="website" |
| 58 | + twitterCard="summary_large_image" |
| 59 | + keywords={["speakers", "tech conference", "barcelona", "2025"]} |
| 60 | +/> |
| 61 | +``` |
| 62 | + |
| 63 | +### Dynamic Metadata |
| 64 | + |
| 65 | +```tsx |
| 66 | +export const SpeakerDetailPage = ({ speaker }) => { |
| 67 | + return ( |
| 68 | + <> |
| 69 | + <PageMetadata |
| 70 | + title={`${speaker.name} - DevBcn Speaker`} |
| 71 | + description={speaker.bio} |
| 72 | + canonicalUrl={`https://www.devbcn.com/speakers/${speaker.id}`} |
| 73 | + ogImage={speaker.profileImage} |
| 74 | + ogType="article" |
| 75 | + /> |
| 76 | + |
| 77 | + {/* Speaker content */} |
| 78 | + </> |
| 79 | + ); |
| 80 | +}; |
| 81 | +``` |
| 82 | + |
| 83 | +## Props Reference |
| 84 | + |
| 85 | +| Prop | Type | Required | Default | Description | |
| 86 | +| -------------- | ------------------------------------ | -------- | ----------------------- | -------------------------- | ----------------------- | |
| 87 | +| `title` | `string` | ✅ | - | Page title (will append " | DevBcn" if not present) | |
| 88 | +| `description` | `string` | ✅ | - | Page description for SEO | |
| 89 | +| `canonicalUrl` | `string` | ❌ | - | Canonical URL for the page | |
| 90 | +| `ogImage` | `string` | ❌ | Default OG image | Open Graph image URL | |
| 91 | +| `ogType` | `"website" \| "article" \| "event"` | ❌ | `"website"` | Open Graph type | |
| 92 | +| `twitterCard` | `"summary" \| "summary_large_image"` | ❌ | `"summary_large_image"` | Twitter card type | |
| 93 | +| `keywords` | `string[]` | ❌ | `[]` | SEO keywords | |
| 94 | + |
| 95 | +## Adding New Pages to Configuration |
| 96 | + |
| 97 | +To add a new page to the centralized configuration: |
| 98 | + |
| 99 | +1. Open `src/config/metadata.ts` |
| 100 | +2. Add your page to the `METADATA_CONFIG` object: |
| 101 | + |
| 102 | +```typescript |
| 103 | +export const METADATA_CONFIG = { |
| 104 | + // ... existing pages |
| 105 | + |
| 106 | + myNewPage: { |
| 107 | + title: "My New Page - DevBcn", |
| 108 | + description: "Description of my new page", |
| 109 | + canonicalUrl: `${BASE_URL}/my-new-page`, |
| 110 | + ogImage: `${BASE_URL}/images/my-new-page.jpg`, |
| 111 | + keywords: ["keyword1", "keyword2"], |
| 112 | + }, |
| 113 | +}; |
| 114 | +``` |
| 115 | + |
| 116 | +3. Use it in your component: |
| 117 | + |
| 118 | +```tsx |
| 119 | +<PageMetadata {...getPageMetadata("myNewPage")} /> |
| 120 | +``` |
| 121 | + |
| 122 | +## Migration from react-helmet |
| 123 | + |
| 124 | +If you're migrating from `react-helmet`, here's the comparison: |
| 125 | + |
| 126 | +### Before (react-helmet) |
| 127 | + |
| 128 | +```tsx |
| 129 | +import { Helmet } from "react-helmet"; |
| 130 | + |
| 131 | +<Helmet> |
| 132 | + <title>My Page | DevBcn</title> |
| 133 | + <meta name="description" content="My description" /> |
| 134 | + <link rel="canonical" href="https://www.devbcn.com/my-page" /> |
| 135 | +</Helmet>; |
| 136 | +``` |
| 137 | + |
| 138 | +### After (React 19 Native) |
| 139 | + |
| 140 | +```tsx |
| 141 | +import { PageMetadata } from "@components/common/PageMetadata"; |
| 142 | + |
| 143 | +<PageMetadata |
| 144 | + title="My Page" |
| 145 | + description="My description" |
| 146 | + canonicalUrl="https://www.devbcn.com/my-page" |
| 147 | +/>; |
| 148 | +``` |
| 149 | + |
| 150 | +## Benefits |
| 151 | + |
| 152 | +✅ **No External Dependencies** - Uses React 19's native features |
| 153 | +✅ **Type-Safe** - Full TypeScript support |
| 154 | +✅ **Centralized** - All metadata in one configuration file |
| 155 | +✅ **Consistent** - Automatic title formatting |
| 156 | +✅ **SEO-Friendly** - Includes all essential meta tags |
| 157 | +✅ **Social Media Ready** - Open Graph and Twitter cards included |
| 158 | + |
| 159 | +## Testing |
| 160 | + |
| 161 | +The component includes comprehensive tests. To run them: |
| 162 | + |
| 163 | +```bash |
| 164 | +npm test -- PageMetadata |
| 165 | +``` |
| 166 | + |
| 167 | +## Best Practices |
| 168 | + |
| 169 | +1. **Always provide title and description** - These are essential for SEO |
| 170 | +2. **Use the centralized config** - Keeps metadata consistent |
| 171 | +3. **Include canonical URLs** - Helps with duplicate content issues |
| 172 | +4. **Provide OG images** - Improves social media sharing |
| 173 | +5. **Use descriptive keywords** - But don't keyword stuff |
| 174 | +6. **Keep descriptions under 160 characters** - For optimal display in search results |
| 175 | +7. **Make titles unique** - Each page should have a distinct title |
| 176 | + |
| 177 | +## Examples by Page Type |
| 178 | + |
| 179 | +### Home Page |
| 180 | + |
| 181 | +```tsx |
| 182 | +<PageMetadata {...getPageMetadata("home2025")} /> |
| 183 | +``` |
| 184 | + |
| 185 | +### Content Page (Speakers, Talks, etc.) |
| 186 | + |
| 187 | +```tsx |
| 188 | +<PageMetadata {...getPageMetadata("speakers")} /> |
| 189 | +``` |
| 190 | + |
| 191 | +### Dynamic Detail Page |
| 192 | + |
| 193 | +```tsx |
| 194 | +<PageMetadata |
| 195 | + title={`${item.title} - DevBcn`} |
| 196 | + description={item.description} |
| 197 | + canonicalUrl={`https://www.devbcn.com/${item.slug}`} |
| 198 | + ogImage={item.image} |
| 199 | +/> |
| 200 | +``` |
| 201 | + |
| 202 | +## Troubleshooting |
| 203 | + |
| 204 | +**Q: The title doesn't appear in the browser tab** |
| 205 | +A: Make sure the `PageMetadata` component is rendered. Check React DevTools to verify it's in the component tree. |
| 206 | + |
| 207 | +**Q: Meta tags aren't showing in view source** |
| 208 | +A: React 19's metadata is rendered client-side. For SSR, you'll need a framework like Next.js. |
| 209 | + |
| 210 | +**Q: Can I use multiple PageMetadata components?** |
| 211 | +A: Only use one per page. The last one rendered will take precedence. |
| 212 | + |
| 213 | +**Q: How do I test if metadata is working?** |
| 214 | +A: Use browser DevTools to inspect the `<head>` element, or use tools like [metatags.io](https://metatags.io/) to preview social cards. |
0 commit comments