Skip to content

Commit

Permalink
feat(seo): imporved SEO support
Browse files Browse the repository at this point in the history
feat: added nextjs metada
  • Loading branch information
a0v0 authored May 12, 2024
2 parents cb8805b + 61c5348 commit 541c2c2
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 46 deletions.
45 changes: 44 additions & 1 deletion app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,60 @@ import {Footer} from "@/components/footer";
import {Navbar} from "@/components/Navbar";
import {fontSans} from "@/config/fonts";
import {routes as manifest} from "@/config/routes";
import {siteConfig} from "@/config/site";
import {getToolByHref} from "@/config/tools";
import "@/styles/globals.css";
import "@/styles/sandpack.css";
import {__PROD__} from "@/utils";
import {getPathnameFromMetadataState} from "@/utils/links";
import {clsx} from "@nextui-org/shared-utils";
import {Analytics} from "@vercel/analytics/react";
import {Metadata} from "next";
import {Providers} from "./providers";

export async function generateMetadata(_: any, state: any): Promise<Metadata> {
// TODO: migrate to a better solution once nextjs allows reading pathname in generateMetadata
const pathname = getPathnameFromMetadataState(state);
const tool = getToolByHref(pathname ?? "");
var title = `${siteConfig.name}${siteConfig.tagline}`;
var description = siteConfig.description;

if (tool) {
title = `${tool.title}${siteConfig.name}`;
description = tool.description;
} else if (pathname === "/tools") {
title = `Tools • ${siteConfig.name}`;
description = "All the available tools in aVToolz.";
}

return {
title: title,
description: description,
keywords: siteConfig.keywords,
themeColor: [
{media: "(prefers-color-scheme: light)", color: "white"},
{media: "(prefers-color-scheme: dark)", color: "black"},
],
icons: {
icon: "/favicon.ico",
},
manifest: "/manifest.json",
openGraph: siteConfig.openGraph,
alternates: {
canonical: "https://avtoolz.com",
types: {
"application/rss+xml": [{url: "https://avtoolz.com/feed.xml", title: "aVToolz RSS Feed"}],
},
},
viewport:
"viewport-fit=cover, width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0",
};
}

export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html suppressHydrationWarning dir="ltr" lang="en">
<head />
{/* <head /> */}
<body className={clsx("min-h-screen bg-background font-sans antialiased", fontSans.variable)}>
<BackgroundEffects />
<Providers themeProps={{attribute: "class", defaultTheme: "dark"}}>
Expand Down
34 changes: 6 additions & 28 deletions app/tools/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
"use client";
import {getToolByHref} from "@/config/tools";
import BuildCircleRoundedIcon from "@mui/icons-material/BuildCircleRounded";
import GridViewRoundedIcon from "@mui/icons-material/GridViewRounded";
import HomeRoundedIcon from "@mui/icons-material/HomeRounded";
import {BreadcrumbItem, Breadcrumbs} from "@nextui-org/react";
import {usePathname, useRouter} from "next/navigation";
export default function ToolLayout({children}: {children: React.ReactNode}) {
const path = usePathname();
const tool = getToolByHref(path);
const router = useRouter();
import Breadcrumb from "@/components/breadcrumb";

export default function ToolLayout({children}: {children: React.ReactNode}) {
return (
<main className="h-screen overflow-auto px-2">
<Breadcrumbs className="flex justify-center mt-4">
<BreadcrumbItem href="/" startContent={<HomeRoundedIcon fontSize="small" />}>
Home
</BreadcrumbItem>

<BreadcrumbItem href="/tools" startContent={<GridViewRoundedIcon fontSize="small" />}>
Tools
</BreadcrumbItem>
{path.includes("/tools/") ? (
<BreadcrumbItem startContent={<BuildCircleRoundedIcon fontSize="small" />}>
{tool?.title}
</BreadcrumbItem>
) : null}
</Breadcrumbs>
{children}
</main>
<>
<Breadcrumb />
<main className="h-screen overflow-auto px-2">{children}</main>
</>
);
}
28 changes: 15 additions & 13 deletions app/tools/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import {Tools} from "@/config/tools";
function page() {
return (
// TODO: add a mini navbar to search, sort and filter tools
<main className="container mx-auto max-w-7xl px-6 flex-grow mt-10">
<section className="flex flex-col items-center justify-center">
<FeaturesGrid
classNames={{
base: "lg:grid-cols-3",
iconWrapper: "bg-transparent",
header: "pt-2",
body: "pt-0 pb-2",
}}
features={Tools}
/>
</section>
</main>
<>
<main className="container mx-auto max-w-7xl px-6 flex-grow mt-10">
<section className="flex flex-col items-center justify-center">
<FeaturesGrid
classNames={{
base: "lg:grid-cols-3",
iconWrapper: "bg-transparent",
header: "pt-2",
body: "pt-0 pb-2",
}}
features={Tools}
/>
</section>
</main>
</>
);
}

Expand Down
8 changes: 7 additions & 1 deletion components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,13 @@ export const Navbar: FC<NavbarProps> = ({routes, slug, tag}) => {
}}
>
{category.routes.map((tool, index) => (
<DropdownItem key={index} href={tool.href} startContent={tool.icon}>
<DropdownItem
className="text-white"
as={Link}
key={index}
href={tool.href}
startContent={tool.icon}
>
{tool.title}
</DropdownItem>
))}
Expand Down
31 changes: 31 additions & 0 deletions components/breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"use client";
import {getToolByHref} from "@/config/tools";
import BuildCircleRoundedIcon from "@mui/icons-material/BuildCircleRounded";
import GridViewRoundedIcon from "@mui/icons-material/GridViewRounded";
import HomeRoundedIcon from "@mui/icons-material/HomeRounded";
import {BreadcrumbItem, Breadcrumbs} from "@nextui-org/react";
import {usePathname} from "next/navigation";

function Breadcrumb() {
const path = usePathname();
const tool = getToolByHref(path);

return (
<Breadcrumbs className="flex justify-center mt-4">
<BreadcrumbItem href="/" startContent={<HomeRoundedIcon fontSize="small" />}>
Home
</BreadcrumbItem>

<BreadcrumbItem href="/tools" startContent={<GridViewRoundedIcon fontSize="small" />}>
Tools
</BreadcrumbItem>
{path.includes("/tools/") ? (
<BreadcrumbItem startContent={<BuildCircleRoundedIcon fontSize="small" />}>
{tool?.title}
</BreadcrumbItem>
) : null}
</Breadcrumbs>
);
}

export default Breadcrumb;
20 changes: 17 additions & 3 deletions config/site.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
export type SiteConfig = typeof siteConfig;

export const siteConfig = {
name: "aVToolz • Fast, beautiful and modern tools for everyone.",
name: "aVToolz",
tagline: "Fast, beautiful and modern tools for everyone.",
description: "Fast, beautiful and modern tools for everyone.",
ogImage: "https://avtoolz.com/twitter-cards/avtoolz.jpeg",
author: "Anubhav Mahur",
email: "",
siteUrl: "https://avtoolz.com",
creator: "@a0v0",
keywords: [
"aVToolz",
"pdf-converter",
"image-converter",
"image-editor",
"pdf-editor",
"text-tools",
"audio-tools",
"video-tools",
"web-tools",
"developer-tools",
"ai-tools",
"AI",
],
openGraph: {
type: "website",
locale: "en_US",
Expand All @@ -16,6 +29,7 @@ export const siteConfig = {
description: "Fast, beautiful and modern tools for everyone.",
images: [
{
// TODO: add og image
url: "https://avtoolz.com/twitter-cards/avtoolz.jpeg",
width: 1200,
height: 630,
Expand Down
18 changes: 18 additions & 0 deletions utils/links.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
export const isActive = (pathname: string | undefined | null, href: string) =>
pathname && pathname.startsWith(href);

/**
*
* Get the pathname from the metadata state
* This dives into async storage of promise state to get the pathname
*
* This is much more performant that using headers() from next as this doesn't opt out from the cache
* @param state
*
* credit: https://github.com/vercel/next.js/discussions/50189#discussioncomment-9224262
*/
export const getPathnameFromMetadataState = (state: any): string => {
const res = Object.getOwnPropertySymbols(state || {})
.map((p) => state[p])
.find((state) => state?.hasOwnProperty?.("urlPathname"));

return res?.urlPathname.replace(/\?.+/, "") ?? "";
};

0 comments on commit 541c2c2

Please sign in to comment.