Skip to content

Commit 7875ea9

Browse files
committed
fix: more seo stuff v2
1 parent 1a092f9 commit 7875ea9

File tree

10 files changed

+173
-262
lines changed

10 files changed

+173
-262
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fezcodex",
3-
"version": "0.8.6",
3+
"version": "0.8.7",
44
"private": true,
55
"homepage": "https://fezcode.com",
66
"dependencies": {

src/hooks/useSeo.js

Lines changed: 121 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -16,252 +16,163 @@ function useSeo({
1616
const location = useLocation();
1717

1818
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)
27+
if (!title || title === 'Fezcodex' || title === 'fezcodex') return;
28+
1929
// Set document title
20-
if (title) {
21-
document.title = title;
22-
}
30+
document.title = title;
2331

2432
// Set meta description
2533
if (description) {
26-
const metaDescription = document.querySelector(
27-
'meta[name="description"]',
28-
);
29-
if (metaDescription) {
30-
metaDescription.setAttribute('content', description);
31-
} else {
32-
const newMeta = document.createElement('meta');
33-
newMeta.setAttribute('name', 'description');
34-
newMeta.setAttribute('content', description);
35-
document.head.appendChild(newMeta);
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);
3639
}
40+
metaDescription.setAttribute('content', description);
3741
}
3842

3943
// Set meta keywords
4044
if (keywords) {
41-
const metaKeywords = document.querySelector('meta[name="keywords"]');
42-
if (metaKeywords) {
43-
metaKeywords.setAttribute('content', keywords);
44-
} else {
45-
const newMeta = document.createElement('meta');
46-
newMeta.setAttribute('name', 'keywords');
47-
newMeta.setAttribute('content', keywords);
48-
document.head.appendChild(newMeta);
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);
4950
}
51+
metaKeywords.setAttribute('content', Array.isArray(keywords) ? keywords.join(', ') : keywords);
5052
}
5153

5254
// Determine default images for apps
5355
const isAppPath = location.pathname.startsWith('/apps');
5456
const defaultAppImage = '/images/asset/ogtitle-apps.png';
55-
const finalOgImage = ogImage || (isAppPath ? defaultAppImage : null);
56-
const finalTwitterImage =
57-
twitterImage || (isAppPath ? defaultAppImage : null);
57+
const ogImagePath = ogImage || (isAppPath ? defaultAppImage : null);
58+
const twitterImagePath = twitterImage || (isAppPath ? defaultAppImage : null);
5859

59-
// Set Open Graph meta tags
60-
if (ogTitle) {
61-
const metaOgTitle = document.querySelector('meta[property="og:title"]');
62-
if (metaOgTitle) {
63-
metaOgTitle.setAttribute('content', ogTitle);
64-
} else {
65-
const newMeta = document.createElement('meta');
66-
newMeta.setAttribute('property', 'og:title');
67-
newMeta.setAttribute('content', ogTitle);
68-
document.head.appendChild(newMeta);
69-
}
70-
}
60+
const finalOgImage = toAbsoluteUrl(ogImagePath);
61+
const finalTwitterImage = toAbsoluteUrl(twitterImagePath);
7162

72-
if (ogDescription) {
73-
const metaOgDescription = document.querySelector(
74-
'meta[property="og:description"]',
75-
);
76-
if (metaOgDescription) {
77-
metaOgDescription.setAttribute('content', ogDescription);
78-
} else {
79-
const newMeta = document.createElement('meta');
80-
newMeta.setAttribute('property', 'og:description');
81-
newMeta.setAttribute('content', ogDescription);
82-
document.head.appendChild(newMeta);
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+
};
72+
73+
Object.entries(ogTags).forEach(([prop, content]) => {
74+
if (!content) return;
75+
let el = document.querySelector(`meta[property="${prop}"]`);
76+
if (!el) {
77+
el = document.createElement('meta');
78+
el.setAttribute('property', prop);
79+
document.head.appendChild(el);
8380
}
84-
}
85-
86-
if (finalOgImage) {
87-
const metaOgImage = document.querySelector('meta[property="og:image"]');
88-
if (metaOgImage) {
89-
metaOgImage.setAttribute('content', finalOgImage);
90-
} else {
91-
const newMeta = document.createElement('meta');
92-
newMeta.setAttribute('property', 'og:image');
93-
newMeta.setAttribute('content', finalOgImage);
94-
document.head.appendChild(newMeta);
81+
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);
9590
}
91+
el.setAttribute('content', finalOgImage);
9692
}
9793

9894
// Set Twitter card meta tags
99-
if (twitterCard) {
100-
const metaTwitterCard = document.querySelector(
101-
'meta[name="twitter:card"]',
102-
);
103-
if (metaTwitterCard) {
104-
metaTwitterCard.setAttribute('content', twitterCard);
105-
} else {
106-
const newMeta = document.createElement('meta');
107-
newMeta.setAttribute('name', 'twitter:card');
108-
newMeta.setAttribute('content', twitterCard);
109-
document.head.appendChild(newMeta);
110-
}
111-
}
112-
113-
if (twitterTitle) {
114-
const metaTwitterTitle = document.querySelector(
115-
'meta[name="twitter:title"]',
116-
);
117-
if (metaTwitterTitle) {
118-
metaTwitterTitle.setAttribute('content', twitterTitle);
119-
} else {
120-
const newMeta = document.createElement('meta');
121-
newMeta.setAttribute('name', 'twitter:title');
122-
newMeta.setAttribute('content', twitterTitle);
123-
document.head.appendChild(newMeta);
124-
}
125-
}
126-
127-
if (twitterDescription) {
128-
const metaTwitterDescription = document.querySelector(
129-
'meta[name="twitter:description"]',
130-
);
131-
if (metaTwitterDescription) {
132-
metaTwitterDescription.setAttribute('content', twitterDescription);
133-
} else {
134-
const newMeta = document.createElement('meta');
135-
newMeta.setAttribute('name', 'twitter:description');
136-
newMeta.setAttribute('content', twitterDescription);
137-
document.head.appendChild(newMeta);
138-
}
139-
}
140-
141-
if (finalTwitterImage) {
142-
const metaTwitterImage = document.querySelector(
143-
'meta[name="twitter:image"]',
144-
);
145-
if (metaTwitterImage) {
146-
metaTwitterImage.setAttribute('content', finalTwitterImage);
147-
} else {
148-
const newMeta = document.createElement('meta');
149-
newMeta.setAttribute('name', 'twitter:image');
150-
newMeta.setAttribute('content', finalTwitterImage);
151-
document.head.appendChild(newMeta);
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,
101+
};
102+
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);
152110
}
153-
}
154-
155-
// Update URL meta tags (Canonical and OG URL)
156-
const currentUrl = window.location.origin + location.pathname;
157-
158-
// OG URL
159-
const metaOgUrl = document.querySelector('meta[property="og:url"]');
160-
if (metaOgUrl) {
161-
metaOgUrl.setAttribute('content', currentUrl);
162-
} else {
163-
const newMeta = document.createElement('meta');
164-
newMeta.setAttribute('property', 'og:url');
165-
newMeta.setAttribute('content', currentUrl);
166-
document.head.appendChild(newMeta);
167-
}
168-
169-
// Twitter URL
170-
const metaTwitterUrl = document.querySelector('meta[name="twitter:url"]');
171-
if (metaTwitterUrl) {
172-
metaTwitterUrl.setAttribute('content', currentUrl);
173-
} else {
174-
const newMeta = document.createElement('meta');
175-
newMeta.setAttribute('name', 'twitter:url');
176-
newMeta.setAttribute('content', currentUrl);
177-
document.head.appendChild(newMeta);
178-
}
111+
el.setAttribute('content', content);
112+
});
179113

180114
// Canonical link
181115
let canonicalLink = document.querySelector('link[rel="canonical"]');
182-
if (canonicalLink) {
183-
canonicalLink.setAttribute('href', currentUrl);
184-
} else {
116+
if (!canonicalLink) {
185117
canonicalLink = document.createElement('link');
186-
canonicalLink.setAttribute('rel', 'canonical');
187-
canonicalLink.setAttribute('href', currentUrl);
188-
document.head.appendChild(canonicalLink);
189-
}
190-
191-
return () => {
192-
// Cleanup: Restore defaults from index.html on unmount
193-
const defaults = {
194-
title: 'fezcodex',
195-
description: 'codex by fezcode...',
196-
ogTitle: 'Fezcodex - Personal Blog and Projects',
197-
ogDescription:
198-
'Discover logs, posts, projects, and stories from Fezcode.',
199-
ogImage: '/images/asset/ogtitle.png',
200-
ogUrl: 'https://fezcode.com/',
201-
twitterCard: 'summary_large_image',
202-
twitterTitle: 'Fezcodex - Personal Blog and Projects',
203-
twitterDescription:
204-
'Discover logs, posts, projects, and stories from Fezcode.',
205-
twitterImage: '/images/asset/ogtitle.png',
206-
twitterUrl: 'https://fezcode.com/',
207-
};
208-
209-
document.title = defaults.title;
210-
211-
const metaDescription = document.querySelector(
212-
'meta[name="description"]',
213-
);
214-
if (metaDescription)
215-
metaDescription.setAttribute('content', defaults.description);
118+
canonicalLink.setAttribute('rel', 'canonical');
119+
document.head.appendChild(canonicalLink);
120+
}
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);
216143

217-
const metaOgTitle = document.querySelector('meta[property="og:title"]');
218-
if (metaOgTitle) metaOgTitle.setAttribute('content', defaults.ogTitle);
144+
const metaOgTitle = document.querySelector('meta[property="og:title"]');
145+
if (metaOgTitle) metaOgTitle.setAttribute('content', defaults.ogTitle);
219146

220-
const metaOgDescription = document.querySelector(
221-
'meta[property="og:description"]',
222-
);
223-
if (metaOgDescription)
224-
metaOgDescription.setAttribute('content', defaults.ogDescription);
147+
const metaOgDescription = document.querySelector('meta[property="og:description"]');
148+
if (metaOgDescription) metaOgDescription.setAttribute('content', defaults.ogDescription);
225149

226-
const metaOgImage = document.querySelector('meta[property="og:image"]');
227-
if (metaOgImage) metaOgImage.setAttribute('content', defaults.ogImage);
150+
const metaOgImage = document.querySelector('meta[property="og:image"]');
151+
if (metaOgImage) metaOgImage.setAttribute('content', defaults.ogImage);
228152

229-
const metaOgUrl = document.querySelector('meta[property="og:url"]');
230-
if (metaOgUrl) metaOgUrl.setAttribute('content', defaults.ogUrl);
153+
const metaOgUrl = document.querySelector('meta[property="og:url"]');
154+
if (metaOgUrl) metaOgUrl.setAttribute('content', defaults.ogUrl);
231155

232-
const metaTwitterCard = document.querySelector(
233-
'meta[name="twitter:card"]',
234-
);
235-
if (metaTwitterCard)
236-
metaTwitterCard.setAttribute('content', defaults.twitterCard);
156+
const metaTwitterCard = document.querySelector('meta[name="twitter:card"]');
157+
if (metaTwitterCard) metaTwitterCard.setAttribute('content', defaults.twitterCard);
237158

238-
const metaTwitterTitle = document.querySelector(
239-
'meta[name="twitter:title"]',
240-
);
241-
if (metaTwitterTitle)
242-
metaTwitterTitle.setAttribute('content', defaults.twitterTitle);
159+
const metaTwitterTitle = document.querySelector('meta[name="twitter:title"]');
160+
if (metaTwitterTitle) metaTwitterTitle.setAttribute('content', defaults.twitterTitle);
243161

244-
const metaTwitterDescription = document.querySelector(
245-
'meta[name="twitter:description"]',
246-
);
247-
if (metaTwitterDescription)
248-
metaTwitterDescription.setAttribute('content', defaults.twitterDescription);
162+
const metaTwitterDescription = document.querySelector('meta[name="twitter:description"]');
163+
if (metaTwitterDescription) metaTwitterDescription.setAttribute('content', defaults.twitterDescription);
249164

250-
const metaTwitterImage = document.querySelector(
251-
'meta[name="twitter:image"]',
252-
);
253-
if (metaTwitterImage)
254-
metaTwitterImage.setAttribute('content', defaults.twitterImage);
165+
const metaTwitterImage = document.querySelector('meta[name="twitter:image"]');
166+
if (metaTwitterImage) metaTwitterImage.setAttribute('content', defaults.twitterImage);
255167

256-
const metaTwitterUrl = document.querySelector('meta[name="twitter:url"]');
257-
if (metaTwitterUrl)
258-
metaTwitterUrl.setAttribute('content', defaults.twitterUrl);
168+
const metaTwitterUrl = document.querySelector('meta[name="twitter:url"]');
169+
if (metaTwitterUrl) metaTwitterUrl.setAttribute('content', defaults.twitterUrl);
259170

260-
const canonicalLink = document.querySelector('link[rel="canonical"]');
261-
if (canonicalLink) canonicalLink.setAttribute('href', defaults.ogUrl);
262-
};
263-
}, [
264-
location.pathname,
171+
const canonicalLink = document.querySelector('link[rel="canonical"]');
172+
if (canonicalLink) canonicalLink.setAttribute('href', defaults.ogUrl);
173+
};
174+
}, [
175+
location.pathname,
265176
title,
266177
description,
267178
keywords,
@@ -275,4 +186,4 @@ function useSeo({
275186
]);
276187
}
277188

278-
export default useSeo;
189+
export default useSeo;

0 commit comments

Comments
 (0)