-
-
-
-
Icons
-
Search our collection of {icons.length} icons.
-
-
-
+ const gallerySchema = {
+ "@context": "https://schema.org",
+ "@type": "ImageGallery",
+ "name": `${SITE_NAME} - Browse ${icons.length} Icons - ${SITE_TAGLINE}`,
+ "description": getBrowseDescription(icons.length),
+ "url": `${WEB_URL}/icons`,
+ "numberOfItems": icons.length,
+ "creator": {
+ "@type": "Organization",
+ "name": ORGANIZATION_NAME,
+ "url": GITHUB_URL
+ }
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+
+
Icons
+
Search our collection of {icons.length} icons - {SITE_TAGLINE}.
+
+
+
+
+
-
+ >
)
}
diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx
index 910ef52b..aec3e3e4 100644
--- a/web/src/app/layout.tsx
+++ b/web/src/app/layout.tsx
@@ -7,8 +7,9 @@ import type { Metadata, Viewport } from "next"
import { Inter } from "next/font/google"
import { Toaster } from "sonner"
import "./globals.css"
-import { getDescription, websiteTitle } from "@/constants"
+import { DEFAULT_KEYWORDS, DEFAULT_OG_IMAGE, GITHUB_URL, ORGANIZATION_NAME, ORGANIZATION_SCHEMA, SITE_NAME, SITE_TAGLINE, WEB_URL, getDescription, getWebsiteSchema, websiteFullTitle, websiteTitle } from "@/constants"
import { ThemeProvider } from "./theme-provider"
+import Script from "next/script"
const inter = Inter({
variable: "--font-inter",
@@ -27,12 +28,13 @@ export const viewport: Viewport = {
export async function generateMetadata(): Promise
{
const { totalIcons } = await getTotalIcons()
+ const description = getDescription(totalIcons)
return {
- metadataBase: new URL("https://dashboardicons.com"),
+ metadataBase: new URL(WEB_URL),
title: websiteTitle,
- description: getDescription(totalIcons),
- keywords: ["dashboard icons", "service icons", "application icons", "tool icons", "web dashboard", "app directory"],
+ description,
+ keywords: DEFAULT_KEYWORDS,
robots: {
index: true,
follow: true,
@@ -42,33 +44,23 @@ export async function generateMetadata(): Promise {
googleBot: "index, follow",
},
openGraph: {
- siteName: "Dashboard Icons",
+ siteName: SITE_NAME,
type: "website",
locale: "en_US",
- title: websiteTitle,
- description: getDescription(totalIcons),
- url: "https://dashboardicons.com",
- images: [
- {
- url: "/og-image.png",
- width: 1200,
- height: 630,
- alt: "Dashboard Icons",
- type: "image/png",
- },
- ],
+ title: websiteFullTitle,
+ description,
+ url: WEB_URL,
+ images: [DEFAULT_OG_IMAGE],
},
twitter: {
card: "summary_large_image",
- site: "@homarr_app",
- creator: "@homarr_app",
- title: websiteTitle,
- description: getDescription(totalIcons),
- images: ["/og-image.png"],
+ title: websiteFullTitle,
+ description,
+ images: [DEFAULT_OG_IMAGE.url],
},
- applicationName: "Dashboard Icons",
+ applicationName: SITE_NAME,
appleWebApp: {
- title: "Dashboard Icons",
+ title: SITE_NAME,
statusBarStyle: "default",
capable: true,
},
@@ -88,12 +80,32 @@ export async function generateMetadata(): Promise {
],
},
manifest: "/site.webmanifest",
+ authors: [{ name: ORGANIZATION_NAME, url: GITHUB_URL }],
+ creator: ORGANIZATION_NAME,
+ publisher: ORGANIZATION_NAME,
+ archives: [`${WEB_URL}/icons`],
+ category: "Icons",
+ classification: "Dashboard Design Resources",
+ other: {
+ "revisit-after": "7 days",
+ },
}
}
-export default function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
+export default async function RootLayout({ children }: Readonly<{ children: React.ReactNode }>) {
+ const { totalIcons } = await getTotalIcons()
+ const websiteSchema = getWebsiteSchema(totalIcons)
+
return (
+
+
+
+
diff --git a/web/src/app/page.tsx b/web/src/app/page.tsx
index 2682ff6c..c498d9f3 100644
--- a/web/src/app/page.tsx
+++ b/web/src/app/page.tsx
@@ -1,42 +1,37 @@
import { HeroSection } from "@/components/hero"
import { RecentlyAddedIcons } from "@/components/recently-added-icons"
-import { BASE_URL, REPO_NAME, getDescription, websiteTitle } from "@/constants"
+import { BASE_URL, DEFAULT_KEYWORDS, DEFAULT_OG_IMAGE, GITHUB_URL, ORGANIZATION_NAME, ORGANIZATION_SCHEMA, SITE_NAME, SITE_TAGLINE, WEB_URL, REPO_NAME, getHomeDescription, websiteFullTitle, websiteTitle } from "@/constants"
import { getRecentlyAddedIcons, getTotalIcons } from "@/lib/api"
import type { Metadata } from "next"
+import Script from "next/script"
export async function generateMetadata(): Promise {
const { totalIcons } = await getTotalIcons()
+ const description = getHomeDescription(totalIcons)
return {
title: websiteTitle,
- description: getDescription(totalIcons),
- keywords: ["dashboard icons", "service icons", "application icons", "tool icons", "web dashboard", "app directory"],
+ description,
+ keywords: DEFAULT_KEYWORDS,
robots: {
index: true,
follow: true,
},
openGraph: {
- title: websiteTitle,
- description: getDescription(totalIcons),
+ title: websiteFullTitle,
+ description,
type: "website",
- url: BASE_URL,
- images: [
- {
- url: "/og-image.png",
- width: 1200,
- height: 630,
- alt: "Dashboard Icons",
- },
- ],
+ url: WEB_URL,
+ images: [DEFAULT_OG_IMAGE],
},
twitter: {
- title: websiteTitle,
- description: getDescription(totalIcons),
+ title: websiteFullTitle,
+ description,
card: "summary_large_image",
- images: ["/og-image.png"],
+ images: [DEFAULT_OG_IMAGE.url],
},
alternates: {
- canonical: BASE_URL,
+ canonical: WEB_URL,
},
}
}
@@ -53,10 +48,38 @@ export default async function Home() {
const recentIcons = await getRecentlyAddedIcons(10)
const stars = await getGitHubStars()
+ // Collection schema for the homepage
+ const collectionSchema = {
+ "@context": "https://schema.org",
+ "@type": "CollectionPage",
+ "name": `${SITE_NAME} Collection - ${SITE_TAGLINE}`,
+ "description": getHomeDescription(totalIcons),
+ "url": WEB_URL,
+ "numberOfItems": totalIcons,
+ "mainEntity": {
+ "@type": "CreativeWork",
+ "name": SITE_NAME,
+ "description": getHomeDescription(totalIcons),
+ "creator": {
+ "@type": "Organization",
+ "name": ORGANIZATION_NAME,
+ "url": GITHUB_URL
+ }
+ }
+ }
+
return (
-
-
-
-
+ <>
+
+
+
+
+
+
+ >
)
}
diff --git a/web/src/constants.ts b/web/src/constants.ts
index acb3d62b..5c90b6e3 100644
--- a/web/src/constants.ts
+++ b/web/src/constants.ts
@@ -4,7 +4,119 @@ export const METADATA_URL = "https://raw.githubusercontent.com/homarr-labs/dashb
export const WEB_URL = "https://dashboardicons.com"
export const REPO_NAME = "homarr-labs/dashboard-icons"
-export const getDescription = (totalIcons: number) =>
- `Collection of ${totalIcons} icons for applications, services, and tools - designed for dashboards and app directories.`
+// Site-wide metadata constants
+export const SITE_NAME = "Dashboard Icons"
+export const TITLE_SEPARATOR = " — "
+export const SITE_TAGLINE = "Your definitive source for dashboard icons"
+export const ORGANIZATION_NAME = "Homarr Labs"
-export const websiteTitle = "Free Dashboard Icons - Download High-Quality UI & App Icons"
+export const getDescription = (totalIcons: number) =>
+ `A curated collection of ${totalIcons} free icons for dashboards and app directories. Available in SVG, PNG, and WEBP formats. ${SITE_TAGLINE}.`
+
+export const getHomeDescription = (totalIcons: number) =>
+ `Discover our curated collection of ${totalIcons} icons designed specifically for dashboards and app directories. ${SITE_TAGLINE}.`
+
+export const getBrowseDescription = (totalIcons: number) =>
+ `Browse, search and download from our collection of ${totalIcons} curated icons. All icons available in SVG, PNG, and WEBP formats. ${SITE_TAGLINE}.`
+
+export const getIconDescription = (iconName: string, totalIcons: number) =>
+ `Download the ${iconName} icon in SVG, PNG, and WEBP formats. Part of our curated collection of ${totalIcons} free icons for dashboards. ${SITE_TAGLINE}.`
+
+export const websiteTitle = `${SITE_NAME} ${TITLE_SEPARATOR} Free, Curated Icons for Apps & Services`
+export const websiteFullTitle = `${SITE_NAME} ${TITLE_SEPARATOR} Free, Curated Icons for Apps & Services ${TITLE_SEPARATOR} ${SITE_TAGLINE}`
+
+// Various keyword sets for different pages
+export const DEFAULT_KEYWORDS = [
+ "dashboard icons",
+ "app icons",
+ "service icons",
+ "curated icons",
+ "free icons",
+ "SVG icons",
+ "web dashboard",
+ "app directory"
+]
+
+export const BROWSE_KEYWORDS = [
+ "browse icons",
+ "search icons",
+ "download icons",
+ "minimal icons",
+ "dashboard design",
+ "UI icons",
+ ...DEFAULT_KEYWORDS
+]
+
+export const ICON_DETAIL_KEYWORDS = (iconName: string) => [
+ `${iconName} icon`,
+ `${iconName} logo`,
+ `${iconName} svg`,
+ `${iconName} download`,
+ `${iconName} dashboard icon`,
+ ...DEFAULT_KEYWORDS
+]
+
+// Core structured data for the website (JSON-LD)
+export const getWebsiteSchema = (totalIcons: number) => ({
+ "@context": "https://schema.org",
+ "@type": "WebSite",
+ "name": SITE_NAME,
+ "url": WEB_URL,
+ "description": getDescription(totalIcons),
+ "potentialAction": {
+ "@type": "SearchAction",
+ "target": {
+ "@type": "EntryPoint",
+ "urlTemplate": `${WEB_URL}/icons?q={search_term_string}`
+ },
+ "query-input": "required name=search_term_string"
+ },
+ "slogan": SITE_TAGLINE
+})
+
+// Organization schema
+export const ORGANIZATION_SCHEMA = {
+ "@context": "https://schema.org",
+ "@type": "Organization",
+ "name": ORGANIZATION_NAME,
+ "url": `https://github.com/${REPO_NAME}`,
+ "logo": `${WEB_URL}/og-image.png`,
+ "sameAs": [
+ `https://github.com/${REPO_NAME}`,
+ "https://homarr.dev"
+ ],
+ "slogan": SITE_TAGLINE
+}
+
+// Social media
+export const GITHUB_URL = `https://github.com/${REPO_NAME}`
+
+// Image schemas
+export const getIconSchema = (iconName: string, iconId: string, authorName: string, authorUrl: string, updateDate: string, totalIcons: number) => ({
+ "@context": "https://schema.org",
+ "@type": "ImageObject",
+ "name": `${iconName} Icon`,
+ "description": getIconDescription(iconName, totalIcons),
+ "contentUrl": `${BASE_URL}/png/${iconId}.png`,
+ "thumbnailUrl": `${BASE_URL}/png/${iconId}.png`,
+ "uploadDate": updateDate,
+ "author": {
+ "@type": "Person",
+ "name": authorName,
+ "url": authorUrl
+ },
+ "encodingFormat": ["image/png", "image/svg+xml", "image/webp"],
+ "contentSize": "Variable",
+ "representativeOfPage": true,
+ "creditText": `Icon contributed by ${authorName} to the ${SITE_NAME} collection by ${ORGANIZATION_NAME}`,
+ "embedUrl": `${WEB_URL}/icons/${iconId}`
+})
+
+// OpenGraph defaults
+export const DEFAULT_OG_IMAGE = {
+ url: "/og-image.png",
+ width: 1200,
+ height: 630,
+ alt: `${SITE_NAME} - ${SITE_TAGLINE}`,
+ type: "image/png"
+}