A comparison of metadata configurations between Lobechat and Shadcn/ui

In this article, you will learn how metadata is configured in Lobechat and Shadcn/ui. This comparison shows two ways to configure your metadata, key difference here is that Shadcn/ui is a UI components provider. You don’t see any API calls made to the backend and you don’t find any database involved either. Lobechat, on the other hand, is our team’s favorite and is quite complex and a large project that has a database, uses tRPC to make API calls.
You will find out how the files and folders are used to configure metadata depending on the context, more on this in the later parts of this article.
There are two ways you can define metadata in a Next.js layout or a page
Config-based Metadata: Export a static metadata object or a dynamic generateMetadata function in a layout.js or page.js file.
File-based Metadata: Add static or dynamically generated special files to route segments.
Read more about:
- Static metadata
- Dynamic metadata
- File Based Metadata
With this information from the documentation, we will find out which way Shadcn/ui and Lobechat have chosen.
Shadcn/ui metadata configuration
In the www/app/layout.tsx in Shadcn/ui source code, metadata is defined as shown below:
export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s - ${siteConfig.name}`,
},
metadataBase: new URL(siteConfig.url),
description: siteConfig.description,
keywords: [
"Next.js",
"React",
"Tailwind CSS",
"Server Components",
"Radix UI",
],
authors: [
{
name: "shadcn",
url: "https://shadcn.com",
},
],
creator: "shadcn",
openGraph: {
type: "website",
locale: "en_US",
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
siteName: siteConfig.name,
images: [
{
url: siteConfig.ogImage,
width: 1200,
height: 630,
alt: siteConfig.name,
},
],
},
twitter: {
card: "summary_large_image",
title: siteConfig.name,
description: siteConfig.description,
images: [siteConfig.ogImage],
creator: "@shadcn",
},
icons: {
icon: "/favicon.ico",
shortcut: "/favicon-16x16.png",
apple: "/apple-touch-icon.png",
},
manifest: `${siteConfig.url}/site.webmanifest`,
}
What this means is that Shadcn/ui uses static metadata. SiteConfig is imported as shown below:
import { META_THEME_COLORS, siteConfig } from "@/config/site"
Lobechat metadata configuration
In the Lobechat/src/app/layout.tsx, you will find the below code:
export { generateMetadata } from './metadata';
generateMetadata here means that Lobechat uses dynamic metadata. Below is the code picked from Lobechat metadata file.
import { Metadata } from 'next';
import { appEnv } from '@/config/app';
import { BRANDING_LOGO_URL, BRANDING_NAME, ORG_NAME } from '@/const/branding';
import { OFFICIAL_URL, OG_URL } from '@/const/url';
import { isCustomBranding, isCustomORG } from '@/const/version';
import { translation } from '@/server/translation';
const BASE_PATH = appEnv.NEXT_PUBLIC_BASE_PATH;
// if there is a base path, then we don't need the manifest
const noManifest = !!BASE_PATH;
export const generateMetadata = async (): Promise<Metadata> => {
const { t } = await translation('metadata');
return {
alternates: {
canonical: OFFICIAL_URL,
},
appleWebApp: {
statusBarStyle: 'black-translucent',
title: BRANDING_NAME,
},
description: t('chat.description', { appName: BRANDING_NAME }),
icons: isCustomBranding
? BRANDING_LOGO_URL
: {
apple: '/apple-touch-icon.png?v=1',
icon: '/favicon.ico?v=1',
shortcut: '/favicon-32x32.ico?v=1',
},
manifest: noManifest ? undefined : '/manifest.json',
metadataBase: new URL(OFFICIAL_URL),
openGraph: {
description: t('chat.description', { appName: BRANDING_NAME }),
images: [
{
alt: t('chat.title', { appName: BRANDING_NAME }),
height: 640,
url: OG_URL,
width: 1200,
},
],
locale: 'en-US',
siteName: BRANDING_NAME,
title: BRANDING_NAME,
type: 'website',
url: OFFICIAL_URL,
},
title: {
default: t('chat.title', { appName: BRANDING_NAME }),
template: `%s · ${BRANDING_NAME}`,
},
twitter: {
card: 'summary_large_image',
description: t('chat.description', { appName: BRANDING_NAME }),
images: [OG_URL],
site: isCustomORG ? `@${ORG_NAME}` : '@lobehub',
title: t('chat.title', { appName: BRANDING_NAME }),
},
};
};
In the Shadcn/ui siteconfig, we saw it contains brand information and relevant urls, but Lobechat has done it differently. There’s a const
folder containing files such as
This configuration in the Lobechat comes from a folder named const instead of config, like in Shadcn/ui, because config folder in Lobechat
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com
My Github - https://github.com/ramu-narasinga My website - https://ramunarasinga.com My Youtube channel - https://www.youtube.com/@thinkthroo Learning platform - https://thinkthroo.com Codebase Architecture - https://app.thinkthroo.com/architecture Best practices - https://app.thinkthroo.com/best-practices Production-grade projects - https://app.thinkthroo.com/production-grade-projects
References:
1. https://github.com/lobehub/lobe-chat/blob/main/src/app/metadata.ts
2. https://github.com/shadcn-ui/ui/blob/main/apps/www/config/site.ts
3. https://nextjs.org/docs/app/building-your-application/optimizing/metadata#static-metadata
4. https://github.com/lobehub/lobe-chat/blob/main/src/app/layout.tsx#L47
5. https://nextjs.org/docs/app/building-your-application/optimizing/metadata#dynamic-metadata
Subscribe to my newsletter
Read articles from Ramu Narasinga directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ramu Narasinga
Ramu Narasinga
I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.