diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 518d6ce..cfacf74 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -19,6 +19,7 @@
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
+ "react-helmet-async": "^3.0.0",
"react-use": "^17.6.0",
"shadcn": "^4.0.6",
"tailwind-merge": "^3.5.0",
@@ -6291,6 +6292,15 @@
"css-in-js-utils": "^3.1.0"
}
},
+ "node_modules/invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.0.0"
+ }
+ },
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
@@ -6991,6 +7001,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -7955,6 +7977,26 @@
"react": "^19.2.4"
}
},
+ "node_modules/react-fast-compare": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
+ "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-helmet-async": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-3.0.0.tgz",
+ "integrity": "sha512-nA3IEZfXiclgrz4KLxAhqJqIfFDuvzQwlKwpdmzZIuC1KNSghDEIXmyU0TKtbM+NafnkICcwx8CECFrZ/sL/1w==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "invariant": "^2.2.4",
+ "react-fast-compare": "^3.2.2",
+ "shallowequal": "^1.1.0"
+ },
+ "peerDependencies": {
+ "react": "^16.6.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-remove-scroll": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz",
@@ -8489,6 +8531,12 @@
"url": "https://github.com/sponsors/colinhacks"
}
},
+ "node_modules/shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==",
+ "license": "MIT"
+ },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 3b99dd0..78ec1af 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -21,6 +21,7 @@
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
+ "react-helmet-async": "^3.0.0",
"react-use": "^17.6.0",
"shadcn": "^4.0.6",
"tailwind-merge": "^3.5.0",
diff --git a/frontend/src/components/SEO.tsx b/frontend/src/components/SEO.tsx
new file mode 100644
index 0000000..f2ac0cf
--- /dev/null
+++ b/frontend/src/components/SEO.tsx
@@ -0,0 +1,64 @@
+import { Helmet } from 'react-helmet-async';
+import { useLocation } from 'react-router-dom';
+
+interface SEOProps {
+ title: string;
+ description: string;
+ canonical?: string;
+ image?: string;
+ type?: string;
+ robots?: string;
+ keywords?: string;
+ twitterCard?: string;
+}
+
+export default function SEO({
+ title,
+ description,
+ canonical,
+ image,
+ type = 'website',
+ robots = 'index,follow',
+ keywords,
+ twitterCard = 'summary_large_image',
+}: SEOProps) {
+ const location = useLocation();
+ const siteUrl = 'https://codex-iter.in';
+ const url = canonical || `${siteUrl}${location.pathname}`;
+
+ // Fallback to a global default social preview image if none provided
+ const defaultImage = `${siteUrl}/codex_dark.png`; // Using the existing icon as default
+ const ogImage = image || defaultImage;
+
+ return (
+
+ {/* Basic Metadata */}
+ {title}
+
+
+ {keywords && }
+
+ {/* Canonical URL */}
+
+
+ {/* Open Graph / Facebook */}
+
+
+
+
+
+
+
+
+ {/* Twitter */}
+
+
+
+
+
+
+ {/* Viewport & Language (usually in index.html, but safe to reinforce if needed) */}
+
+
+ );
+}
diff --git a/frontend/src/components/layout/Footer.tsx b/frontend/src/components/layout/Footer.tsx
index 5c72d8c..d209cea 100644
--- a/frontend/src/components/layout/Footer.tsx
+++ b/frontend/src/components/layout/Footer.tsx
@@ -78,13 +78,15 @@ export default function Footer() {
Connect
{[
- { icon:
, href: settings?.linkedin || "#", id: "linkedin" },
- { icon:
, href: settings?.email ? `mailto:${settings.email}` : "#", id: "email" },
- { icon:
, href: settings?.instagram || "#", id: "instagram" },
+ { icon:
, href: settings?.linkedin || "https://www.linkedin.com/company/codex-iter", id: "linkedin" },
+ { icon:
, href: settings?.email ? `mailto:${settings.email}` : "mailto:codexiter@gmail.com", id: "email" },
+ { icon:
, href: settings?.instagram || "https://www.instagram.com/codexiter", id: "instagram" },
].map(({ icon, href, id }) => (
- {scrolled ?

:

}
+ {scrolled ?

:

}
CODEX ITER
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index bef5202..0a361e5 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -1,10 +1,13 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
+import { HelmetProvider } from 'react-helmet-async'
import './index.css'
import App from './App.tsx'
createRoot(document.getElementById('root')!).render(
-
+
+
+
,
)
diff --git a/frontend/src/pages/BlogDetail.tsx b/frontend/src/pages/BlogDetail.tsx
index 853ec3e..c00a10a 100644
--- a/frontend/src/pages/BlogDetail.tsx
+++ b/frontend/src/pages/BlogDetail.tsx
@@ -4,6 +4,7 @@ import { client } from "../sanity/client";
import { urlFor } from "../sanity/image";
import { PortableText } from "@portabletext/react";
import { ScrollReveal } from "../components/animations/ScrollReveal";
+import SEO from "../components/SEO";
export default function BlogDetail() {
const { slug } = useParams<{ slug: string }>();
@@ -58,6 +59,13 @@ export default function BlogDetail() {
return (
+
diff --git a/frontend/src/pages/Blogs.tsx b/frontend/src/pages/Blogs.tsx
index a9e788b..3577e68 100644
--- a/frontend/src/pages/Blogs.tsx
+++ b/frontend/src/pages/Blogs.tsx
@@ -5,6 +5,7 @@ import { ScrollReveal, StaggerContainer, StaggerItem } from "../components/anima
import { useEffect, useState } from "react";
import { client } from "../sanity/client";
import { urlFor } from "../sanity/image";
+import SEO from "../components/SEO";
export default function Blogs() {
const { hero } = mockData.blogs;
@@ -68,6 +69,10 @@ export default function Blogs() {
return (
+
{/* Header Section */}
diff --git a/frontend/src/pages/Events.tsx b/frontend/src/pages/Events.tsx
index 55e8800..6117ddf 100644
--- a/frontend/src/pages/Events.tsx
+++ b/frontend/src/pages/Events.tsx
@@ -6,6 +6,7 @@ import TiltCard from "../components/animations/TiltCard";
import { useEffect, useState } from "react";
import { client } from "../sanity/client";
import { urlFor } from "../sanity/image";
+import SEO from "../components/SEO";
const prefersReduced =
typeof window !== "undefined" &&
@@ -194,6 +195,10 @@ export default function Events() {
return (
+
{/* Header */}
@@ -201,9 +206,9 @@ export default function Events() {
{hero.label}
-
+
{hero.titlePart1}
{hero.titlePart2}
-
+
{hero.description}
diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx
index 0c7feae..e2531d0 100644
--- a/frontend/src/pages/Home.tsx
+++ b/frontend/src/pages/Home.tsx
@@ -6,6 +6,7 @@ import { ScrollReveal, StaggerContainer, StaggerItem } from "../components/anima
import main_logo from "../assets/main_logo.png"
import { client } from "../sanity/client";
import { urlFor } from "../sanity/image";
+import SEO from "../components/SEO";
const prefersReduced =
typeof window !== "undefined" &&
@@ -134,6 +135,10 @@ export default function Home() {
return (
+
{/* ── HERO ────────────────────────────────────────────────── */}
@@ -147,7 +152,7 @@ export default function Home() {
{hero.established}
-
+
{" "}
@@ -155,7 +160,7 @@ export default function Home() {
{" "}
-
+
{/* Typing subheading */}
@@ -199,7 +204,7 @@ export default function Home() {
-

+
{hero.loadingText}
diff --git a/frontend/src/pages/Team.tsx b/frontend/src/pages/Team.tsx
index 89ee295..c3c3666 100644
--- a/frontend/src/pages/Team.tsx
+++ b/frontend/src/pages/Team.tsx
@@ -5,6 +5,7 @@ import TiltCard from "../components/animations/TiltCard";
import { useEffect, useState, useRef } from "react";
import { client } from "../sanity/client";
import { urlFor } from "../sanity/image";
+import SEO from "../components/SEO";
const prefersReduced =
typeof window !== "undefined" &&
@@ -152,6 +153,10 @@ export default function Team() {
return (
+
{/* Hero */}
@@ -159,9 +164,9 @@ export default function Team() {
{hero.label}
-
+
{hero.titlePart1}
{hero.titlePart2}
-
+
{hero.description}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 1efc49a..4d70c58 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -9,8 +9,6 @@ export default defineConfig({
sitemap({
hostname: 'https://codex-iter.in',
dynamicRoutes: [
- '/',
- '/about',
'/blogs',
'/events',
'/team'