Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(saas): Improved SEO, Metadata to pages #30

Merged
merged 1 commit into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,38 @@ import { getBlogs } from "@/server/actions/blog";
import { format } from "date-fns";
import Image from "next/image";
import { notFound, redirect } from "next/navigation";
import { type Metadata } from "next";

export const dynamic = "force-static";

type BlogSlugPageProps = {
params: {
slug: string[];
slug: string;
};
};

export async function generateMetadata({
params,
}: BlogSlugPageProps): Promise<Metadata> {
const slug = params.slug;

const blog = (await getBlogs()).find((b) => b.metaData.slug === slug);

if (!blog) {
return notFound();
}

return {
title: blog.metaData.title,
description: blog.metaData.description,
};
}

export async function generateStaticParams() {
const blogs = await getBlogs();

return blogs.map((blog) => ({
slug: blog.metaData.slug.split("/"),
slug: blog.metaData.slug,
}));
}

Expand All @@ -27,7 +45,7 @@ export default async function BlogSlugPage({ params }: BlogSlugPageProps) {
return redirect(siteUrls.blog);
}

const slug = params.slug.join("/");
const slug = params.slug;

const blog = (await getBlogs()).find((b) => b.metaData.slug === slug);

Expand Down
3 changes: 3 additions & 0 deletions starterkits/saas/src/app/(web)/blog/_constants/page-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const blogPageConfig = {
title: "Blog",
} as const;
6 changes: 6 additions & 0 deletions starterkits/saas/src/app/(web)/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import { getBlogs } from "@/server/actions/blog";
import { format } from "date-fns";
import Image from "next/image";
import Link from "next/link";
import { type Metadata } from "next";
import { blogPageConfig } from "@/app/(web)/blog/_constants/page-config";

export const metadata: Metadata = {
title: blogPageConfig.title,
};

export const dynamic = "force-static";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const changelogPageConfig = {
title: "Change Log",
} as const;
13 changes: 8 additions & 5 deletions starterkits/saas/src/app/(web)/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ import { siteConfig } from "@/config/site";
import { getChangelogs } from "@/server/actions/changelog";
import { format } from "date-fns";
import Image from "next/image";
import type { Metadata } from "next";
import { changelogPageConfig } from "@/app/(web)/changelog/_constants/page-config";

export const metadata: Metadata = {
title: changelogPageConfig.title,
};

export const dynamic = "force-static";

Expand All @@ -35,11 +41,8 @@ export default async function ChangeLogPage() {
</p>
</WebPageHeader>
<div className="grid w-full max-w-4xl gap-8">
{changelogs.map((changelog) => (
<ChangeLogCard
key={changelog.metaData.slug}
{...changelog}
/>
{changelogs.map((changelog, index) => (
<ChangeLogCard key={index} {...changelog} />
))}
</div>
</WebPageWrapper>
Expand Down
7 changes: 6 additions & 1 deletion starterkits/saas/src/app/(web)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import { siteUrls } from "@/config/urls";
import Image from "next/image";
import Link from "next/link";
import Balancer from "react-wrap-balancer";
import type { Metadata } from "next";

export const metadata: Metadata = {
title: "Build Your MVP in Days, not weeks. Next.js Starter Kit",
};

export const dynamic = "force-static";

Expand All @@ -19,7 +24,7 @@ export default async function HomePage() {
<WebPageWrapper>
<WebPageHeader
badge="Launch your saas in 24 hours"
title="Rapidly launch your MVP with Beautiful Starterkits, Blocks, and more."
title="Build Your MVP in Days, not weeks. Open Source Starter Kit"
>
<Balancer
as="p"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const pricingPageConfig = {
title: "Pricing",
} as const;
6 changes: 6 additions & 0 deletions starterkits/saas/src/app/(web)/pricing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
WebPageHeader,
WebPageWrapper,
} from "@/app/(web)/_components/general-components";
import { type Metadata } from "next";
import { pricingPageConfig } from "@/app/(web)/pricing/_constants/page-config";

/**
* Customize the pricing page to your needs. You can use the `PricingPlans` component to display the pricing plans.
Expand All @@ -11,6 +13,10 @@ import {
* To customize the pricing plans, you can modify the `PricingPlans` component. @see /app/(web)/pricing/components/pricing-plans.tsx
*/

export const metadata: Metadata = {
title: pricingPageConfig.title,
};

export default function PricingPage() {
return (
<WebPageWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { siteConfig } from "@/config/site";

export const supportPageConfig = {
title: "Support",
description: `Get support from ${siteConfig.name} to get started building your next project.`,
} as const;
12 changes: 11 additions & 1 deletion starterkits/saas/src/app/(web)/support/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,21 @@ import {
import { type SupportInfo, supportInfos } from "@/config/support";
import { ArrowRightIcon } from "lucide-react";
import Link from "next/link";
import { type Metadata } from "next";
import { supportPageConfig } from "@/app/(web)/support/_constants/page-config";

export const metadata: Metadata = {
title: supportPageConfig.title,
description: supportPageConfig.description,
};

export default function ContactPage() {
return (
<WebPageWrapper>
<WebPageHeader title="Support for You" badge="Get in touch with us">
<WebPageHeader
title={supportPageConfig.title}
badge="Get in touch with us"
>
<p>
If you have any questions or need help, feel free to reach
out to us.
Expand Down
6 changes: 6 additions & 0 deletions starterkits/saas/src/app/auth/login/_constants/page-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { siteConfig } from "@/config/site";

export const loginPageConfig = {
title: "Login",
description: `Login to ${siteConfig.name} to get started building your next project.`,
} as const;
7 changes: 7 additions & 0 deletions starterkits/saas/src/app/auth/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { AuthForm } from "@/app/auth/_components/auth-form";
import { loginPageConfig } from "@/app/auth/login/_constants/page-config";
import { type Metadata } from "next";

export const metadata: Metadata = {
title: loginPageConfig.title,
description: loginPageConfig.description,
};

export default function Login() {
return <AuthForm type="login" />;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { siteConfig } from "@/config/site";

export const signupPageConfig = {
title: "Signup",
description: `Signup to ${siteConfig.name} to get started building your next project.`,
} as const;
7 changes: 7 additions & 0 deletions starterkits/saas/src/app/auth/signup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { AuthForm } from "@/app/auth/_components/auth-form";
import { signupPageConfig } from "@/app/auth/signup/_constants/page-config";
import { type Metadata } from "next";

export const metadata: Metadata = {
title: signupPageConfig.title,
description: signupPageConfig.description,
};

export default function Signup() {
return <AuthForm type="signup" />;
Expand Down
24 changes: 20 additions & 4 deletions starterkits/saas/src/app/docs/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { notFound } from "next/navigation";
import { Toc } from "@/components/toc";
import { getDocs } from "@/server/actions/docs";
import { type Metadata } from "next";

export const dynamic = "force-static";

Expand All @@ -10,13 +11,26 @@ type DocsSlugPageProps = {
};
};

export async function generateMetadata({
params,
}: DocsSlugPageProps): Promise<Metadata> {
const slug = Array.isArray(params.slug) ? params.slug.join("/") : "/";

const doc = (await getDocs()).find((doc) => doc.metaData.slug === slug);

if (!doc) {
return notFound();
}

return {
title: doc.metaData.title,
description: doc.metaData.description,
};
}

export async function generateStaticParams() {
const docs = await getDocs();

docs.map((doc) => {
console.log(doc.metaData.slug.split("/") || ["/"]);
});

return docs.map((doc) => ({
slug: doc.metaData.slug.split("/") || ["/"],
}));
Expand All @@ -27,6 +41,8 @@ export default async function DocsSlugPage({ params }: DocsSlugPageProps) {

const doc = (await getDocs()).find((doc) => doc.metaData.slug === slug);

console.log(["gettings-started", "installation"].join("/"), params.slug);

if (!doc) {
return notFound();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const invitePageConfig = {
title: ({ orgName }: { orgName: string }) => `Invite to ${orgName}`,
description: ({ orgName }: { orgName: string }) =>
`Invite your team to ${orgName} and get started building your next project.`,
} as const;
18 changes: 17 additions & 1 deletion starterkits/saas/src/app/invite/org/[orgId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { getOrgByIdQuery } from "@/server/actions/organization/queries";
import { RequestCard } from "@/app/invite/org/[orgId]/_components/request-card";
import { notFound } from "next/navigation";
import { type Metadata } from "next";

type OrgRequestProps = {
export type OrgRequestProps = {
params: {
orgId: string;
};
Expand All @@ -23,3 +24,18 @@ export default async function OrgRequestPage({
</main>
);
}

export async function generateMetadata({
params,
}: OrgRequestProps): Promise<Metadata> {
const org = await getOrgByIdQuery({ orgId: params.orgId });

if (!org) {
return notFound();
}

return {
title: `Invite to ${org.name}`,
description: `Invite your team to ${org.name} and get started building your next project.`,
};
}
19 changes: 14 additions & 5 deletions starterkits/saas/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,21 @@ import { Toaster } from "@/components/ui/sonner";
import "@/styles/globals.css";
import "@/styles/prism.css";
import { fontHeading, fontSans } from "@/lib/fonts";
import { type Metadata } from "next";
import {
defaultMetadata,
twitterMetadata,
ogMetadata,
} from "@/app/shared-metadata";

export const metadata = {
title: "RapidLaunch - Next.js Boilerplate",
description:
"Next.js boilerplate with shadcn ui, TRPC, TailwindCSS, and Drizzle.",
icons: [{ rel: "icon", url: "/favicon.ico" }],
export const metadata: Metadata = {
...defaultMetadata,
twitter: {
...twitterMetadata,
},
openGraph: {
...ogMetadata,
},
};

export default function RootLayout({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const maintenancePageConfig = {
title: "Maintenance",
description:
"We&apos;re currently undergoing maintenance. Please check back later.",
} as const;
7 changes: 7 additions & 0 deletions starterkits/saas/src/app/maintenance/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { maintenancePageConfig } from "@/app/maintenance/_constants/page-config";
import { siteConfig } from "@/config/site";
import { type Metadata } from "next";

export const metadata: Metadata = {
title: maintenancePageConfig.title,
description: maintenancePageConfig.description,
};

export default function Maintenance() {
return (
Expand Down
12 changes: 12 additions & 0 deletions starterkits/saas/src/app/robots.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { siteUrls } from "@/config/urls";
import type { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: "/",
},
sitemap: `${siteUrls.publicUrl}/sitemap.xml`,
};
}
39 changes: 39 additions & 0 deletions starterkits/saas/src/app/shared-metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { siteConfig } from "@/config/site";
import { siteUrls } from "@/config/urls";
import type { Metadata } from "next";

export const defaultMetadata: Metadata = {
title: {
template: `%s | ${siteConfig.name}`,
default: siteConfig.name,
},
description: siteConfig.description,
metadataBase: new URL(siteUrls.publicUrl),
keywords: [
"Next.js",
"React",
"Next.js Starter kit",
"SaaS Starter Kit",
"Shadcn UI",
],
authors: [{ name: "Ali Farooq", url: "https://twitter.com/alifarooqdev" }],
creator: "AliFarooqDev",
};

export const twitterMetadata: Metadata["twitter"] = {
title: siteConfig.name,
description: siteConfig.description,
card: "summary_large_image",
images: [siteConfig.orgImage],
creator: "@alifarooqdev",
};

export const ogMetadata: Metadata["openGraph"] = {
title: siteConfig.name,
description: siteConfig.description,
type: "website",
images: [{ url: siteConfig.orgImage, alt: siteConfig.name }],
locale: "en_US",
url: siteUrls.publicUrl,
siteName: siteConfig.name,
};
Loading
Loading