Skip to content

Commit e8b7a29

Browse files
committed
fix: more seo stuff v3
1 parent 7875ea9 commit e8b7a29

10 files changed

+75
-229
lines changed

src/hooks/useSeo.js

Lines changed: 59 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,189 +1,84 @@
11
import { useEffect } from 'react';
22
import { useLocation } from 'react-router-dom';
33

4-
function useSeo({
5-
title,
6-
description,
7-
keywords,
8-
ogTitle,
9-
ogDescription,
10-
ogImage,
11-
twitterCard,
12-
twitterTitle,
13-
twitterDescription,
14-
twitterImage,
15-
}) {
4+
const BASE_URL = 'https://fezcode.com';
5+
6+
/**
7+
* useSeo - A simplified hook for managing site metadata.
8+
* Standardizes Title, Description, and Image across all platforms (OG, Twitter, etc.)
9+
*/
10+
function useSeo({ title, description, image, keywords, ogImage, twitterImage }) {
1611
const location = useLocation();
1712

1813
useEffect(() => {
19-
// Helper to ensure absolute URLs
20-
const toAbsoluteUrl = (path) => {
21-
if (!path) return path;
22-
if (path.startsWith('http')) return path;
23-
return window.location.origin + (path.startsWith('/') ? '' : '/') + path;
24-
};
25-
26-
// Only update if we have a real title (prevents snapping loading state defaults too early)
14+
// 1. Only update if we have a valid title
2715
if (!title || title === 'Fezcodex' || title === 'fezcodex') return;
2816

29-
// Set document title
30-
document.title = title;
31-
32-
// Set meta description
33-
if (description) {
34-
let metaDescription = document.querySelector('meta[name="description"]');
35-
if (!metaDescription) {
36-
metaDescription = document.createElement('meta');
37-
metaDescription.setAttribute('name', 'description');
38-
document.head.appendChild(metaDescription);
39-
}
40-
metaDescription.setAttribute('content', description);
41-
}
42-
43-
// Set meta keywords
44-
if (keywords) {
45-
let metaKeywords = document.querySelector('meta[name="keywords"]');
46-
if (!metaKeywords) {
47-
metaKeywords = document.createElement('meta');
48-
metaKeywords.setAttribute('name', 'keywords');
49-
document.head.appendChild(metaKeywords);
50-
}
51-
metaKeywords.setAttribute('content', Array.isArray(keywords) ? keywords.join(', ') : keywords);
52-
}
53-
54-
// Determine default images for apps
55-
const isAppPath = location.pathname.startsWith('/apps');
56-
const defaultAppImage = '/images/asset/ogtitle-apps.png';
57-
const ogImagePath = ogImage || (isAppPath ? defaultAppImage : null);
58-
const twitterImagePath = twitterImage || (isAppPath ? defaultAppImage : null);
59-
60-
const finalOgImage = toAbsoluteUrl(ogImagePath);
61-
const finalTwitterImage = toAbsoluteUrl(twitterImagePath);
17+
// 2. Prepare standardized values
18+
const currentUrl = BASE_URL + location.pathname;
6219

63-
// Set Open Graph meta tags
64-
const ogTags = {
65-
'og:title': ogTitle || title,
66-
'og:description': ogDescription || description,
67-
'og:image': finalOgImage,
68-
'og:url': window.location.origin + location.pathname,
69-
'og:type': location.pathname.startsWith('/blog') ? 'article' : 'website',
70-
'og:site_name': 'Fezcodex',
71-
};
20+
// Pick the first available image, fallback to default
21+
const rawImage = image || ogImage || twitterImage || '/images/asset/ogtitle.png';
22+
const finalImage = rawImage.startsWith('http')
23+
? rawImage
24+
: BASE_URL + (rawImage.startsWith('/') ? '' : '/') + rawImage;
7225

73-
Object.entries(ogTags).forEach(([prop, content]) => {
26+
// 3. Helper to update/create meta tags
27+
const setMeta = (attrName, attrValue, content) => {
7428
if (!content) return;
75-
let el = document.querySelector(`meta[property="${prop}"]`);
29+
let el = document.querySelector(`meta[${attrName}="${attrValue}"]`);
7630
if (!el) {
7731
el = document.createElement('meta');
78-
el.setAttribute('property', prop);
32+
el.setAttribute(attrName, attrValue);
7933
document.head.appendChild(el);
8034
}
8135
el.setAttribute('content', content);
82-
});
83-
84-
if (finalOgImage?.startsWith('https')) {
85-
let el = document.querySelector('meta[property="og:image:secure_url"]');
86-
if (!el) {
87-
el = document.createElement('meta');
88-
el.setAttribute('property', 'og:image:secure_url');
89-
document.head.appendChild(el);
90-
}
91-
el.setAttribute('content', finalOgImage);
92-
}
93-
94-
// Set Twitter card meta tags
95-
const twitterTags = {
96-
'twitter:card': twitterCard || 'summary_large_image',
97-
'twitter:title': twitterTitle || ogTitle || title,
98-
'twitter:description': twitterDescription || ogDescription || description,
99-
'twitter:image': finalTwitterImage || finalOgImage,
100-
'twitter:url': window.location.origin + location.pathname,
10136
};
10237

103-
Object.entries(twitterTags).forEach(([name, content]) => {
104-
if (!content) return;
105-
let el = document.querySelector(`meta[name="${name}"]`);
106-
if (!el) {
107-
el = document.createElement('meta');
108-
el.setAttribute('name', name);
109-
document.head.appendChild(el);
110-
}
111-
el.setAttribute('content', content);
112-
});
38+
// 4. Update Document Title
39+
document.title = title;
11340

114-
// Canonical link
115-
let canonicalLink = document.querySelector('link[rel="canonical"]');
116-
if (!canonicalLink) {
117-
canonicalLink = document.createElement('link');
118-
canonicalLink.setAttribute('rel', 'canonical');
119-
document.head.appendChild(canonicalLink);
41+
// 5. Update Standard Meta Tags
42+
setMeta('name', 'description', description);
43+
if (keywords) {
44+
setMeta('name', 'keywords', Array.isArray(keywords) ? keywords.join(', ') : keywords);
12045
}
121-
canonicalLink.setAttribute('href', window.location.origin + location.pathname);
122-
123-
return () => {
124-
// Cleanup: Restore defaults from index.html on unmount
125-
const defaults = {
126-
title: 'fezcodex',
127-
description: 'codex by fezcode...',
128-
ogTitle: 'Fezcodex - Personal Blog and Projects',
129-
ogDescription: 'Discover logs, posts, projects, and stories from Fezcode.',
130-
ogImage: toAbsoluteUrl('/images/asset/ogtitle.png'),
131-
ogUrl: 'https://fezcode.com/',
132-
twitterCard: 'summary_large_image',
133-
twitterTitle: 'Fezcodex - Personal Blog and Projects',
134-
twitterDescription: 'Discover logs, posts, projects, and stories from Fezcode.',
135-
twitterImage: toAbsoluteUrl('/images/asset/ogtitle.png'),
136-
twitterUrl: 'https://fezcode.com/',
137-
};
138-
139-
document.title = defaults.title;
140-
141-
const metaDescription = document.querySelector('meta[name="description"]');
142-
if (metaDescription) metaDescription.setAttribute('content', defaults.description);
143-
144-
const metaOgTitle = document.querySelector('meta[property="og:title"]');
145-
if (metaOgTitle) metaOgTitle.setAttribute('content', defaults.ogTitle);
14646

147-
const metaOgDescription = document.querySelector('meta[property="og:description"]');
148-
if (metaOgDescription) metaOgDescription.setAttribute('content', defaults.ogDescription);
149-
150-
const metaOgImage = document.querySelector('meta[property="og:image"]');
151-
if (metaOgImage) metaOgImage.setAttribute('content', defaults.ogImage);
152-
153-
const metaOgUrl = document.querySelector('meta[property="og:url"]');
154-
if (metaOgUrl) metaOgUrl.setAttribute('content', defaults.ogUrl);
155-
156-
const metaTwitterCard = document.querySelector('meta[name="twitter:card"]');
157-
if (metaTwitterCard) metaTwitterCard.setAttribute('content', defaults.twitterCard);
158-
159-
const metaTwitterTitle = document.querySelector('meta[name="twitter:title"]');
160-
if (metaTwitterTitle) metaTwitterTitle.setAttribute('content', defaults.twitterTitle);
161-
162-
const metaTwitterDescription = document.querySelector('meta[name="twitter:description"]');
163-
if (metaTwitterDescription) metaTwitterDescription.setAttribute('content', defaults.twitterDescription);
164-
165-
const metaTwitterImage = document.querySelector('meta[name="twitter:image"]');
166-
if (metaTwitterImage) metaTwitterImage.setAttribute('content', defaults.twitterImage);
47+
// 6. Update Open Graph Tags (Standardized)
48+
setMeta('property', 'og:title', title);
49+
setMeta('property', 'og:description', description);
50+
setMeta('property', 'og:image', finalImage);
51+
if (finalImage.toLowerCase().endsWith('.webp')) {
52+
setMeta('property', 'og:image:type', 'image/webp');
53+
} else if (finalImage.toLowerCase().endsWith('.png')) {
54+
setMeta('property', 'og:image:type', 'image/png');
55+
} else if (finalImage.toLowerCase().endsWith('.jpg') || finalImage.toLowerCase().endsWith('.jpeg')) {
56+
setMeta('property', 'og:image:type', 'image/jpeg');
57+
}
16758

168-
const metaTwitterUrl = document.querySelector('meta[name="twitter:url"]');
169-
if (metaTwitterUrl) metaTwitterUrl.setAttribute('content', defaults.twitterUrl);
59+
setMeta('property', 'og:url', currentUrl);
60+
setMeta('property', 'og:type', location.pathname.startsWith('/blog') ? 'article' : 'website');
61+
setMeta('property', 'og:site_name', 'Fezcodex');
62+
if (finalImage.startsWith('https')) {
63+
setMeta('property', 'og:image:secure_url', finalImage);
64+
}
17065

171-
const canonicalLink = document.querySelector('link[rel="canonical"]');
172-
if (canonicalLink) canonicalLink.setAttribute('href', defaults.ogUrl);
173-
};
174-
}, [
175-
location.pathname,
176-
title,
177-
description,
178-
keywords,
179-
ogTitle,
180-
ogDescription,
181-
ogImage,
182-
twitterCard,
183-
twitterTitle,
184-
twitterDescription,
185-
twitterImage,
186-
]);
66+
// 7. Update Twitter Tags (Standardized)
67+
setMeta('name', 'twitter:card', 'summary_large_image');
68+
setMeta('name', 'twitter:title', title);
69+
setMeta('name', 'twitter:description', description);
70+
setMeta('name', 'twitter:image', finalImage);
71+
setMeta('name', 'twitter:url', currentUrl);
72+
73+
// 8. Update Canonical Link
74+
let canonical = document.querySelector('link[rel="canonical"]');
75+
if (!canonical) {
76+
canonical = document.createElement('link');
77+
canonical.setAttribute('rel', 'canonical');
78+
document.head.appendChild(canonical);
79+
}
80+
canonical.setAttribute('href', currentUrl);
81+
}, [location.pathname, title, description, image, keywords, ogImage, twitterImage]);
18782
}
18883

189-
export default useSeo;
84+
export default useSeo;

src/pages/DokumentBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,8 @@ const DokumentBlogPostPage = () => {
102102
useSeo({
103103
title: post ? `${post.attributes.title} | Fezcodex` : null,
104104
description: post ? post.body.substring(0, 150) : null,
105-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
106-
ogTitle: post ? `${post.attributes.title} | Fezcodex` : null,
107-
ogDescription: post ? post.body.substring(0, 150) : null,
108-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
109-
twitterCard: 'summary_large_image',
110-
twitterTitle: post ? `${post.attributes.title} | Fezcodex` : null,
111-
twitterDescription: post ? post.body.substring(0, 150) : null,
112-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
105+
image: post?.attributes?.ogImage || post?.attributes?.image,
106+
keywords: post?.attributes?.tags,
113107
});
114108

115109
useEffect(() => {

src/pages/DossierBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,8 @@ const DossierBlogPostPage = () => {
106106
useSeo({
107107
title: post ? `${post.attributes.title} | Fezcodex Archive` : null,
108108
description: post ? post.body.substring(0, 150) : null,
109-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
110-
ogTitle: post ? `${post.attributes.title} | Fezcodex` : null,
111-
ogDescription: post ? post.body.substring(0, 150) : null,
112-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
113-
twitterCard: 'summary_large_image',
114-
twitterTitle: post ? `${post.attributes.title} | Fezcodex` : null,
115-
twitterDescription: post ? post.body.substring(0, 150) : null,
116-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
109+
image: post?.attributes?.ogImage || post?.attributes?.image,
110+
keywords: post?.attributes?.tags,
117111
});
118112

119113
useEffect(() => {

src/pages/LogDetailPage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ const LogDetailPage = () => {
3030
useSeo({
3131
title: log ? `${log.attributes.title} | Fezcodex` : null,
3232
description: log ? log.body.substring(0, 150) : null,
33-
ogImage: log?.attributes?.image || '/images/asset/ogtitle.png',
33+
image: log?.attributes?.image,
3434
});
3535

3636
useEffect(() => {

src/pages/OldBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,8 @@ const OldBlogPostPage = () => {
4444
useSeo({
4545
title: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
4646
description: post ? post.body.substring(0, 150) : null,
47-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
48-
ogTitle: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
49-
ogDescription: post ? post.body.substring(0, 150) : null,
50-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
51-
twitterCard: 'summary_large_image',
52-
twitterTitle: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
53-
twitterDescription: post ? post.body.substring(0, 150) : null,
54-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
47+
image: post?.attributes?.ogImage || post?.attributes?.image,
48+
keywords: post?.attributes?.tags,
5549
});
5650

5751
// --- Effects ---

src/pages/ProjectPage.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,9 @@ const ProjectPage = () => {
3030

3131
useSeo({
3232
title: fullProject ? `${fullProject.title} | Fezcodex` : null,
33-
description: fullProject?.shortDescription || null,
34-
keywords: fullProject?.tags ? fullProject.tags.join(', ') : null,
35-
ogTitle: fullProject ? `${fullProject.title} | Fezcodex` : null,
36-
ogDescription: fullProject?.shortDescription || null,
37-
ogImage: fullProject?.image || '/images/asset/ogtitle.png',
38-
twitterCard: 'summary_large_image',
39-
twitterTitle: fullProject ? `${fullProject.title} | Fezcodex` : null,
40-
twitterDescription: fullProject?.shortDescription || null,
41-
twitterImage: fullProject?.image || '/images/asset/ogtitle.png',
33+
description: fullProject?.shortDescription,
34+
image: fullProject?.image,
35+
keywords: fullProject?.tags,
4236
});
4337

4438
if (loadingProjects || loadingContent) {

src/pages/StandardBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,8 @@ const StandardBlogPostPage = () => {
4343
useSeo({
4444
title: post ? `${post.attributes.title} | Fezcodex` : null,
4545
description: post ? post.body.substring(0, 150) : null,
46-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
47-
ogTitle: post ? `${post.attributes.title} | Fezcodex` : null,
48-
ogDescription: post ? post.body.substring(0, 150) : null,
49-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
50-
twitterCard: 'summary_large_image',
51-
twitterTitle: post ? `${post.attributes.title} | Fezcodex` : null,
52-
twitterDescription: post ? post.body.substring(0, 150) : null,
53-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
46+
image: post?.attributes?.ogImage || post?.attributes?.image,
47+
keywords: post?.attributes?.tags,
5448
});
5549

5650
useEffect(() => {

src/pages/TerminalBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,8 @@ const TerminalBlogPostPage = () => {
102102
useSeo({
103103
title: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
104104
description: post ? post.body.substring(0, 150) : null,
105-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
106-
ogTitle: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
107-
ogDescription: post ? post.body.substring(0, 150) : null,
108-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
109-
twitterCard: 'summary_large_image',
110-
twitterTitle: post ? `${post.attributes.title.toUpperCase()} | TERMINAL LOG` : null,
111-
twitterDescription: post ? post.body.substring(0, 150) : null,
112-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
105+
image: post?.attributes?.ogImage || post?.attributes?.image,
106+
keywords: post?.attributes?.tags,
113107
});
114108

115109
useEffect(() => {

src/pages/TerminalGreenBlogPostPage.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,8 @@ const TerminalGreenBlogPostPage = () => {
4747
useSeo({
4848
title: post ? `${post.attributes.title.toUpperCase()} | TERM` : null,
4949
description: post ? post.body.substring(0, 150) : null,
50-
keywords: post?.attributes?.tags ? post.attributes.tags.join(', ') : null,
51-
ogTitle: post ? `${post.attributes.title.toUpperCase()} | TERM` : null,
52-
ogDescription: post ? post.body.substring(0, 150) : null,
53-
ogImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
54-
twitterCard: 'summary_large_image',
55-
twitterTitle: post ? `${post.attributes.title.toUpperCase()} | TERM` : null,
56-
twitterDescription: post ? post.body.substring(0, 150) : null,
57-
twitterImage: post?.attributes?.ogImage || post?.attributes?.image || '/images/asset/ogtitle.png',
50+
image: post?.attributes?.ogImage || post?.attributes?.image,
51+
keywords: post?.attributes?.tags,
5852
});
5953

6054
useEffect(() => {

src/pages/UsefulLinksPage.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,6 @@ const UsefulLinksPage = () => {
1010
title: 'Redirecting... | Fezcodex',
1111
description: 'Redirecting to a random log entry on Fezcodex.',
1212
keywords: ['Fezcodex', 'redirect', 'random', 'log'],
13-
ogTitle: 'Redirecting... | Fezcodex',
14-
ogDescription: 'Redirecting to a random log entry on Fezcodex.',
15-
ogImage: '/images/asset/ogtitle.png',
16-
twitterCard: 'summary_large_image',
17-
twitterTitle: 'Redirecting... | Fezcodex',
18-
twitterDescription: 'Redirecting to a random log entry on Fezcodex.',
19-
twitterImage: '/images/asset/ogtitle.png',
2013
});
2114

2215
useEffect(() => {

0 commit comments

Comments
 (0)