diff --git a/.env.example b/.env.example
index b3625eb..e6eb1e7 100644
--- a/.env.example
+++ b/.env.example
@@ -4,5 +4,5 @@ GEMINI_MODEL=gemini-2.5-flash
NEXT_PUBLIC_SUPABASE_URL=your_supabase_project_url_here
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key_here
SUPABASE_SERVICE_ROLE_KEY=your_supabase_service_role_key_here
-NEXT_PUBLIC_SITE_URL=http://localhost:3000
+NEXT_PUBLIC_SITE_URL=http://localhost:3005
NEXT_PUBLIC_ADMIN_EMAIL=your_admin_email@example.com
diff --git a/app/admin/page.jsx b/app/admin/page.jsx
index 2c774c1..a06120b 100644
--- a/app/admin/page.jsx
+++ b/app/admin/page.jsx
@@ -1,10 +1,12 @@
import AdminDashboardClient from "@/components/AdminDashboardClient";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Admin Dashboard - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Admin Dashboard",
description: "Manage StudyAI users and platform analytics.",
-};
+ path: "/admin",
+});
export default function AdminPage() {
return (
diff --git a/app/ai-notes/page.jsx b/app/ai-notes/page.jsx
index 5b04120..9655a18 100644
--- a/app/ai-notes/page.jsx
+++ b/app/ai-notes/page.jsx
@@ -1,10 +1,12 @@
import AINotesGenerator from "@/components/AINotesGenerator";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "AI Notes - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "AI Notes",
description: "Generate Gemini-powered study notes and save them to StudyAI.",
-};
+ path: "/ai-notes",
+});
export default function AINotesPage() {
return (
diff --git a/app/ai-quiz/page.jsx b/app/ai-quiz/page.jsx
index c4bb84a..79247d7 100644
--- a/app/ai-quiz/page.jsx
+++ b/app/ai-quiz/page.jsx
@@ -1,10 +1,12 @@
import AIQuizGenerator from "@/components/AIQuizGenerator";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "AI Quiz - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "AI Quiz",
description: "Generate interactive Gemini-powered quizzes from any topic or notes.",
-};
+ path: "/ai-quiz",
+});
export default function AIQuizPage() {
return (
diff --git a/app/ai-summary/page.jsx b/app/ai-summary/page.jsx
index b81cfda..5022702 100644
--- a/app/ai-summary/page.jsx
+++ b/app/ai-summary/page.jsx
@@ -1,10 +1,12 @@
import AISummaryGenerator from "@/components/AISummaryGenerator";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "AI Summary - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "AI Summary",
description: "Summarize long study material with Gemini-powered AI.",
-};
+ path: "/ai-summary",
+});
export default function AISummaryPage() {
return (
diff --git a/app/chat/page.jsx b/app/chat/page.jsx
index d4770c4..ea65526 100644
--- a/app/chat/page.jsx
+++ b/app/chat/page.jsx
@@ -1,10 +1,12 @@
import AIChatPageClient from "@/components/AIChatPageClient";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "AI Chat - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "AI Chat",
description: "Chat with StudyAI and save your conversations securely.",
-};
+ path: "/chat",
+});
export default function ChatPage() {
return (
diff --git a/app/dashboard/layout.jsx b/app/dashboard/layout.jsx
new file mode 100644
index 0000000..00d50d1
--- /dev/null
+++ b/app/dashboard/layout.jsx
@@ -0,0 +1,11 @@
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Dashboard",
+ description: "Open your StudyAI workspace.",
+ path: "/dashboard",
+});
+
+export default function DashboardLayout({ children }) {
+ return children;
+}
diff --git a/app/flashcards/page.jsx b/app/flashcards/page.jsx
index 64ac35c..cde647a 100644
--- a/app/flashcards/page.jsx
+++ b/app/flashcards/page.jsx
@@ -1,10 +1,12 @@
import AIFlashcardsGenerator from "@/components/AIFlashcardsGenerator";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Flashcards - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Flashcards",
description: "Generate interactive Gemini-powered flashcards from any topic or notes.",
-};
+ path: "/flashcards",
+});
export default function FlashcardsPage() {
return (
diff --git a/app/forgot-password/page.jsx b/app/forgot-password/page.jsx
index b726861..71895ac 100644
--- a/app/forgot-password/page.jsx
+++ b/app/forgot-password/page.jsx
@@ -1,9 +1,11 @@
import { ForgotPasswordForm } from "@/components/PasswordResetForms";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Forgot Password - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Forgot Password",
description: "Reset your StudyAI password securely.",
-};
+ path: "/forgot-password",
+});
export default function ForgotPasswordPage() {
return ;
diff --git a/app/history/page.jsx b/app/history/page.jsx
index d47bfeb..05b9374 100644
--- a/app/history/page.jsx
+++ b/app/history/page.jsx
@@ -1,10 +1,12 @@
import AIHistoryPageClient from "@/components/AIHistoryPageClient";
import DashboardShell from "@/components/DashboardShell";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "AI History - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "AI History",
description: "Search, filter, reuse, and manage previous StudyAI generations.",
-};
+ path: "/history",
+});
export default function HistoryPage() {
return (
diff --git a/app/layout.jsx b/app/layout.jsx
index 31c42a2..76b3a16 100644
--- a/app/layout.jsx
+++ b/app/layout.jsx
@@ -1,9 +1,62 @@
import "./globals.css";
import ThemeProvider from "@/components/ThemeProvider";
+import {
+ defaultDescription,
+ defaultTitle,
+ seoKeywords,
+ siteName,
+ siteUrl,
+} from "@/lib/seo";
export const metadata = {
- title: "StudyAI - Study Smarter with AI",
- description: "Generate notes, solve questions, and summarize topics instantly.",
+ metadataBase: new URL(siteUrl),
+ applicationName: siteName,
+ title: {
+ default: defaultTitle,
+ template: `%s | ${siteName}`,
+ },
+ description: defaultDescription,
+ keywords: seoKeywords,
+ authors: [{ name: siteName }],
+ creator: siteName,
+ publisher: siteName,
+ category: "Education",
+ alternates: {
+ canonical: "/",
+ },
+ openGraph: {
+ type: "website",
+ locale: "en_US",
+ url: "/",
+ siteName,
+ title: defaultTitle,
+ description: defaultDescription,
+ images: [
+ {
+ url: "/opengraph-image",
+ width: 1200,
+ height: 630,
+ alt: "StudyAI AI study workspace for notes, summaries, quizzes, and flashcards",
+ },
+ ],
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: defaultTitle,
+ description: defaultDescription,
+ images: ["/twitter-image"],
+ },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ "max-video-preview": -1,
+ "max-image-preview": "large",
+ "max-snippet": -1,
+ },
+ },
icons: {
icon: [
{ url: "/icon.svg", type: "image/svg+xml" },
@@ -12,6 +65,7 @@ export const metadata = {
{ url: "/icon.svg", type: "image/svg+xml" },
],
},
+ manifest: "/manifest.webmanifest",
};
export default function RootLayout({ children }) {
diff --git a/app/login/page.jsx b/app/login/page.jsx
index 75de0df..188e243 100644
--- a/app/login/page.jsx
+++ b/app/login/page.jsx
@@ -1,4 +1,11 @@
import AuthForm from "@/components/AuthForm";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Login",
+ description: "Log in to your StudyAI account.",
+ path: "/login",
+});
export default function LoginPage() {
return ;
diff --git a/app/manifest.js b/app/manifest.js
new file mode 100644
index 0000000..c55a401
--- /dev/null
+++ b/app/manifest.js
@@ -0,0 +1,22 @@
+import { defaultDescription, siteName } from "@/lib/seo";
+
+export default function manifest() {
+ return {
+ name: `${siteName} - AI Study Workspace`,
+ short_name: siteName,
+ description: defaultDescription,
+ start_url: "/",
+ scope: "/",
+ display: "standalone",
+ background_color: "#f8fafc",
+ theme_color: "#635bff",
+ icons: [
+ {
+ src: "/icon.svg",
+ sizes: "64x64",
+ type: "image/svg+xml",
+ purpose: "any maskable",
+ },
+ ],
+ };
+}
diff --git a/app/notes/page.jsx b/app/notes/page.jsx
index f782eef..ce309ec 100644
--- a/app/notes/page.jsx
+++ b/app/notes/page.jsx
@@ -1,10 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import SavedNotesPageClient from "@/components/SavedNotesPageClient";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Saved Notes - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Saved Notes",
description: "Search, filter, favorite, and manage saved StudyAI notes, summaries, quizzes, and flashcards.",
-};
+ path: "/notes",
+});
export default function NotesPage() {
return (
diff --git a/app/opengraph-image.jsx b/app/opengraph-image.jsx
new file mode 100644
index 0000000..613ba9d
--- /dev/null
+++ b/app/opengraph-image.jsx
@@ -0,0 +1,125 @@
+import { ImageResponse } from "next/og";
+
+export const alt =
+ "StudyAI - AI study notes, summaries, quizzes, and flashcards";
+export const size = {
+ width: 1200,
+ height: 630,
+};
+export const contentType = "image/png";
+
+export default function Image() {
+ return new ImageResponse(
+ (
+
+
+
+
+ Turn Any Topic Into
+ Smart Study Material.
+
+
+ AI notes, summaries, quizzes, flashcards, and solved questions for
+ focused learners.
+
+
+
+ {["Smart Notes", "Quick Summary", "Quiz Ready", "Flashcards"].map(
+ (label, index) => (
+
+ {label}
+ Ready
+
+ ),
+ )}
+
+
+ ),
+ size,
+ );
+}
diff --git a/app/page.jsx b/app/page.jsx
index e706e86..0a5a399 100644
--- a/app/page.jsx
+++ b/app/page.jsx
@@ -8,20 +8,137 @@ import HowItWorks from "@/components/HowItWorks";
import Navbar from "@/components/Navbar";
import StatsSection from "@/components/StatsSection";
import Testimonials from "@/components/Testimonials";
+import {
+ absoluteUrl,
+ defaultDescription,
+ defaultTitle,
+ jsonLd,
+ siteName,
+ siteUrl,
+} from "@/lib/seo";
+
+const homepageFaqs = [
+ {
+ question: "How does StudyAI work?",
+ answer:
+ "Enter a topic, paste study material, or ask a question. StudyAI uses AI to generate clear notes, summaries, explanations, quizzes, and practice material in seconds.",
+ },
+ {
+ question: "Is StudyAI free?",
+ answer:
+ "Yes. Students can start with StudyAI for free and quickly try AI notes, summaries, quizzes, flashcards, and question solving.",
+ },
+ {
+ question: "Can StudyAI create quizzes and flashcards?",
+ answer:
+ "Yes. StudyAI can turn topics and pasted notes into quiz-style practice questions and interactive flashcards for revision.",
+ },
+ {
+ question: "Can StudyAI help with exam revision?",
+ answer:
+ "Yes. StudyAI helps organize key ideas, definitions, formulas, summaries, and practice questions so students can review faster before exams.",
+ },
+];
+
+const structuredData = [
+ {
+ "@context": "https://schema.org",
+ "@type": "WebApplication",
+ name: siteName,
+ url: siteUrl,
+ applicationCategory: "EducationalApplication",
+ operatingSystem: "Web",
+ description: defaultDescription,
+ offers: {
+ "@type": "Offer",
+ price: "0",
+ priceCurrency: "USD",
+ },
+ featureList: [
+ "AI notes generator",
+ "AI summary generator",
+ "AI quiz generator",
+ "AI flashcards",
+ "Question solver",
+ "Saved study workspace",
+ ],
+ publisher: {
+ "@type": "Organization",
+ name: siteName,
+ url: siteUrl,
+ logo: absoluteUrl("/icon.svg"),
+ },
+ },
+ {
+ "@context": "https://schema.org",
+ "@type": "Organization",
+ name: siteName,
+ url: siteUrl,
+ logo: absoluteUrl("/icon.svg"),
+ },
+ {
+ "@context": "https://schema.org",
+ "@type": "FAQPage",
+ mainEntity: homepageFaqs.map((item) => ({
+ "@type": "Question",
+ name: item.question,
+ acceptedAnswer: {
+ "@type": "Answer",
+ text: item.answer,
+ },
+ })),
+ },
+];
+
+export const metadata = {
+ title: {
+ absolute: defaultTitle,
+ },
+ description: defaultDescription,
+ alternates: {
+ canonical: "/",
+ },
+ openGraph: {
+ title: defaultTitle,
+ description: defaultDescription,
+ url: "/",
+ siteName,
+ images: [
+ {
+ url: "/opengraph-image",
+ width: 1200,
+ height: 630,
+ alt: "StudyAI AI study workspace for notes, summaries, quizzes, and flashcards",
+ },
+ ],
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: defaultTitle,
+ description: defaultDescription,
+ images: ["/twitter-image"],
+ },
+};
export default function Home() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
);
}
diff --git a/app/planner/page.jsx b/app/planner/page.jsx
index 0a08a7f..2b18abe 100644
--- a/app/planner/page.jsx
+++ b/app/planner/page.jsx
@@ -1,5 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import StudyPlanner from "@/components/StudyPlanner";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Study Planner",
+ description: "Create deadlines, set priorities, and track study tasks in StudyAI.",
+ path: "/planner",
+});
export default function PlannerPage() {
return (
diff --git a/app/profile/page.jsx b/app/profile/page.jsx
index f256f9c..ef4fad5 100644
--- a/app/profile/page.jsx
+++ b/app/profile/page.jsx
@@ -1,5 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import ProfilePageClient from "@/components/ProfilePageClient";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Profile",
+ description: "Manage your StudyAI profile and account details.",
+ path: "/profile",
+});
export default function ProfilePage() {
return (
diff --git a/app/questions/page.jsx b/app/questions/page.jsx
index c59d033..1b3fa5f 100644
--- a/app/questions/page.jsx
+++ b/app/questions/page.jsx
@@ -1,5 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import QuestionList from "@/components/QuestionList";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Solved Questions",
+ description: "Review questions solved with StudyAI.",
+ path: "/questions",
+});
export default function QuestionsPage() {
return (
diff --git a/app/reset-password/page.jsx b/app/reset-password/page.jsx
index a99d2a3..8a060e7 100644
--- a/app/reset-password/page.jsx
+++ b/app/reset-password/page.jsx
@@ -1,9 +1,11 @@
import { ResetPasswordForm } from "@/components/PasswordResetForms";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Reset Password - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Reset Password",
description: "Create a new StudyAI password.",
-};
+ path: "/reset-password",
+});
export default function ResetPasswordPage() {
return ;
diff --git a/app/robots.js b/app/robots.js
new file mode 100644
index 0000000..10c5a14
--- /dev/null
+++ b/app/robots.js
@@ -0,0 +1,35 @@
+import { absoluteUrl, siteUrl } from "@/lib/seo";
+
+export default function robots() {
+ return {
+ rules: {
+ userAgent: "*",
+ allow: ["/"],
+ disallow: [
+ "/admin",
+ "/ai-notes",
+ "/ai-quiz",
+ "/ai-summary",
+ "/api",
+ "/chat",
+ "/dashboard",
+ "/flashcards",
+ "/forgot-password",
+ "/history",
+ "/login",
+ "/notes",
+ "/planner",
+ "/profile",
+ "/questions",
+ "/reset-password",
+ "/settings",
+ "/sign-in",
+ "/sign-up",
+ "/signup",
+ "/summaries",
+ ],
+ },
+ sitemap: absoluteUrl("/sitemap.xml"),
+ host: siteUrl,
+ };
+}
diff --git a/app/settings/page.jsx b/app/settings/page.jsx
index 67bcdbf..7696219 100644
--- a/app/settings/page.jsx
+++ b/app/settings/page.jsx
@@ -1,10 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import SettingsPageClient from "@/components/SettingsPageClient";
+import { buildNoIndexMetadata } from "@/lib/seo";
-export const metadata = {
- title: "Settings - StudyAI",
+export const metadata = buildNoIndexMetadata({
+ title: "Settings",
description: "Manage your StudyAI account, appearance, privacy, and security settings.",
-};
+ path: "/settings",
+});
export default function SettingsPage() {
return (
diff --git a/app/sign-in/[[...sign-in]]/page.jsx b/app/sign-in/[[...sign-in]]/page.jsx
index b17b993..e248277 100644
--- a/app/sign-in/[[...sign-in]]/page.jsx
+++ b/app/sign-in/[[...sign-in]]/page.jsx
@@ -1,4 +1,11 @@
import AuthForm from "@/components/AuthForm";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Sign In",
+ description: "Sign in to StudyAI.",
+ path: "/sign-in",
+});
export default function SignInAliasPage() {
return ;
diff --git a/app/sign-up/[[...sign-up]]/page.jsx b/app/sign-up/[[...sign-up]]/page.jsx
index eda0e47..6a82831 100644
--- a/app/sign-up/[[...sign-up]]/page.jsx
+++ b/app/sign-up/[[...sign-up]]/page.jsx
@@ -1,4 +1,11 @@
import AuthForm from "@/components/AuthForm";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Sign Up",
+ description: "Create your StudyAI account.",
+ path: "/sign-up",
+});
export default function SignUpAliasPage() {
return ;
diff --git a/app/signup/page.jsx b/app/signup/page.jsx
index 1a9b397..c5e7b93 100644
--- a/app/signup/page.jsx
+++ b/app/signup/page.jsx
@@ -1,4 +1,11 @@
import AuthForm from "@/components/AuthForm";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Sign Up",
+ description: "Create your free StudyAI account.",
+ path: "/signup",
+});
export default function SignupPage() {
return ;
diff --git a/app/sitemap.js b/app/sitemap.js
new file mode 100644
index 0000000..9e5517f
--- /dev/null
+++ b/app/sitemap.js
@@ -0,0 +1,12 @@
+import { absoluteUrl } from "@/lib/seo";
+
+export default function sitemap() {
+ return [
+ {
+ url: absoluteUrl("/"),
+ lastModified: new Date(),
+ changeFrequency: "weekly",
+ priority: 1,
+ },
+ ];
+}
diff --git a/app/summaries/page.jsx b/app/summaries/page.jsx
index 676c3ed..e3ae029 100644
--- a/app/summaries/page.jsx
+++ b/app/summaries/page.jsx
@@ -1,5 +1,12 @@
import DashboardShell from "@/components/DashboardShell";
import SummaryList from "@/components/SummaryList";
+import { buildNoIndexMetadata } from "@/lib/seo";
+
+export const metadata = buildNoIndexMetadata({
+ title: "Saved Summaries",
+ description: "Review saved StudyAI summaries.",
+ path: "/summaries",
+});
export default function SummariesPage() {
return (
diff --git a/app/twitter-image.jsx b/app/twitter-image.jsx
new file mode 100644
index 0000000..8f726ce
--- /dev/null
+++ b/app/twitter-image.jsx
@@ -0,0 +1 @@
+export { alt, contentType, default, size } from "./opengraph-image";
diff --git a/lib/seo.js b/lib/seo.js
new file mode 100644
index 0000000..58d8cce
--- /dev/null
+++ b/lib/seo.js
@@ -0,0 +1,64 @@
+const DEFAULT_SITE_URL = "https://study-ai.mumar.dev";
+
+function normalizeSiteUrl(url) {
+ const cleanedUrl = String(url || DEFAULT_SITE_URL)
+ .trim()
+ .replace(/^["']|["']$/g, "")
+ .replace(/\/+$/g, "");
+
+ try {
+ return new URL(cleanedUrl).origin;
+ } catch {
+ return DEFAULT_SITE_URL;
+ }
+}
+
+export const siteName = "StudyAI";
+export const siteUrl = normalizeSiteUrl(process.env.NEXT_PUBLIC_SITE_URL);
+export const defaultTitle =
+ "StudyAI - AI Study Notes, Summaries, Quizzes & Flashcards";
+export const defaultDescription =
+ "Turn any topic into clear AI notes, concise summaries, practice quizzes, flashcards, and solved questions with StudyAI.";
+
+export const seoKeywords = [
+ "StudyAI",
+ "AI study tool",
+ "AI notes generator",
+ "AI quiz generator",
+ "AI summary generator",
+ "AI flashcards",
+ "student study app",
+ "exam revision AI",
+ "question solver",
+];
+
+export const noIndexRobots = {
+ index: false,
+ follow: false,
+ nocache: true,
+ googleBot: {
+ index: false,
+ follow: false,
+ noimageindex: true,
+ },
+};
+
+export function absoluteUrl(path = "/") {
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
+ return `${siteUrl}${normalizedPath}`;
+}
+
+export function buildNoIndexMetadata({ title, description, path }) {
+ return {
+ title,
+ description,
+ alternates: {
+ canonical: path,
+ },
+ robots: noIndexRobots,
+ };
+}
+
+export function jsonLd(data) {
+ return JSON.stringify(data).replace(/