Skip to content

Commit abb7bfe

Browse files
committed
Add environment-specific favicons
1 parent 6af0e2a commit abb7bfe

7 files changed

Lines changed: 76 additions & 0 deletions

File tree

public/favicon-development.ico

743 Bytes
Binary file not shown.

public/favicon-preview.ico

745 Bytes
Binary file not shown.

public/favicon.ico

1.13 KB
Binary file not shown.

src/app/favicon.ico

-4.19 KB
Binary file not shown.

src/app/layout.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,31 @@ import '@/styles/globals.css'
44
import { Analytics } from '@vercel/analytics/next'
55
import { SpeedInsights } from '@vercel/speed-insights/next'
66
import Head from 'next/head'
7+
import type { Metadata } from 'next/types'
78
import { Suspense } from 'react'
9+
import {
10+
FAVICON_CONTENT_TYPE,
11+
FAVICON_SIZE,
12+
getFaviconHref,
13+
} from '@/configs/favicon'
814
import ClientProviders from '@/features/client-providers'
915
import { GeneralAnalyticsCollector } from '@/features/general-analytics-collector'
1016
import { GTMHead } from '@/features/google-tag-manager'
1117
import { Toaster } from '@/ui/primitives/toaster'
1218
import { Body } from './body'
1319

20+
export const metadata: Metadata = {
21+
icons: {
22+
icon: [
23+
{
24+
url: getFaviconHref(),
25+
type: FAVICON_CONTENT_TYPE,
26+
sizes: `${FAVICON_SIZE.width}x${FAVICON_SIZE.height}`,
27+
},
28+
],
29+
},
30+
}
31+
1432
export default function RootLayout({
1533
children,
1634
}: {

src/configs/favicon.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export type FaviconEnvironment = 'production' | 'preview' | 'development'
2+
3+
export const FAVICON_SIZE = {
4+
width: 32,
5+
height: 32,
6+
} as const
7+
8+
export const FAVICON_CONTENT_TYPE = 'image/x-icon'
9+
10+
const FAVICON_HREFS = {
11+
production: '/favicon.ico',
12+
preview: '/favicon-preview.ico',
13+
development: '/favicon-development.ico',
14+
} as const satisfies Record<FaviconEnvironment, string>
15+
16+
export function getFaviconEnvironment(
17+
vercelEnv = process.env.VERCEL_ENV
18+
): FaviconEnvironment {
19+
if (vercelEnv === 'production' || vercelEnv === 'preview') {
20+
return vercelEnv
21+
}
22+
23+
return 'development'
24+
}
25+
26+
export function getFaviconHref(vercelEnv = process.env.VERCEL_ENV) {
27+
return FAVICON_HREFS[getFaviconEnvironment(vercelEnv)]
28+
}

tests/unit/favicon.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { afterEach, describe, expect, it, vi } from 'vitest'
2+
import { getFaviconEnvironment, getFaviconHref } from '@/configs/favicon'
3+
4+
describe('environment favicon', () => {
5+
afterEach(() => {
6+
vi.unstubAllEnvs()
7+
})
8+
9+
it('uses the original favicon appearance in production', () => {
10+
expect(getFaviconEnvironment('production')).toBe('production')
11+
expect(getFaviconHref('production')).toBe('/favicon.ico')
12+
})
13+
14+
it('uses amber background in preview', () => {
15+
expect(getFaviconEnvironment('preview')).toBe('preview')
16+
expect(getFaviconHref('preview')).toBe('/favicon-preview.ico')
17+
})
18+
19+
it('uses green background in local development', () => {
20+
expect(getFaviconEnvironment('development')).toBe('development')
21+
expect(getFaviconHref('development')).toBe('/favicon-development.ico')
22+
})
23+
24+
it('falls back to local development when VERCEL_ENV is missing', () => {
25+
vi.stubEnv('VERCEL_ENV', undefined)
26+
27+
expect(getFaviconEnvironment()).toBe('development')
28+
expect(getFaviconHref()).toBe('/favicon-development.ico')
29+
})
30+
})

0 commit comments

Comments
 (0)