From 94fb7c60e0db1eddaa7d0a7499462f42f9a2e85d Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Sun, 22 Feb 2026 19:21:28 +0100 Subject: [PATCH] feat: optimize favicon loading across the application using Next.js image component for better performance and caching --- CHANGELOG.md | 1 + app/share/[id]/page.tsx | 6 +++++- components/dashboard/Campaigns.tsx | 6 +++++- components/dashboard/TopReferrers.tsx | 6 +++++- components/sites/SiteList.tsx | 6 +++++- next.config.ts | 9 +++++++++ 6 files changed, 30 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ba0cd..678b847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - **Smoother loading experience.** Pages now show a subtle preview of the layout while data loads instead of a blank screen or spinner. This applies everywhere — dashboards, settings, uptime, funnels, notifications, billing, and detail modals. - **No more loading flicker.** Fast-loading pages no longer flash a loading state for a split second before showing content. - **Clearer error messages.** When something goes wrong, the error message now tells you what failed (e.g. "Failed to load uptime monitors") instead of a generic "Failed to load data". +- **Faster favicon loading.** Site icons in the dashboard, referrers, and campaigns now use Next.js image optimization for better caching and lazy loading. ## [0.10.0-alpha] - 2026-02-21 diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx index d645fe7..e1a32cc 100644 --- a/app/share/[id]/page.tsx +++ b/app/share/[id]/page.tsx @@ -1,6 +1,7 @@ 'use client' import { useCallback, useEffect, useState } from 'react' +import Image from 'next/image' import { useParams, useSearchParams, useRouter } from 'next/navigation' import { getPublicDashboard, getPublicStats, getPublicDailyStats, getPublicRealtime, getPublicPerformanceByPage, type DashboardData, type Stats, type DailyStat, type PerformanceByPageStat } from '@/lib/api/stats' import { toast } from '@ciphera-net/ui' @@ -282,13 +283,16 @@ export default function PublicDashboardPage() { Public Dashboard

- {site.name} { (e.target as HTMLImageElement).src = '/globe.svg' }} + unoptimized /> {site.domain}

diff --git a/components/dashboard/Campaigns.tsx b/components/dashboard/Campaigns.tsx index f4b5f02..f15efd2 100644 --- a/components/dashboard/Campaigns.tsx +++ b/components/dashboard/Campaigns.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useMemo } from 'react' import Link from 'next/link' +import Image from 'next/image' import { formatNumber } from '@ciphera-net/ui' import { Modal, ArrowRightIcon, Button } from '@ciphera-net/ui' import { TableSkeleton } from '@/components/skeletons' @@ -111,11 +112,14 @@ export default function Campaigns({ siteId, dateRange }: CampaignsProps) { const useFavicon = faviconUrl && !faviconFailed.has(source) if (useFavicon) { return ( - setFaviconFailed((prev) => new Set(prev).add(source))} + unoptimized /> ) } diff --git a/components/dashboard/TopReferrers.tsx b/components/dashboard/TopReferrers.tsx index b4f0974..fbfca1a 100644 --- a/components/dashboard/TopReferrers.tsx +++ b/components/dashboard/TopReferrers.tsx @@ -1,6 +1,7 @@ 'use client' import { useState, useEffect } from 'react' +import Image from 'next/image' import { formatNumber } from '@ciphera-net/ui' import { getReferrerDisplayName, getReferrerFavicon, getReferrerIcon, mergeReferrersByDisplayName } from '@/lib/utils/icons' import { Modal, GlobeIcon } from '@ciphera-net/ui' @@ -39,11 +40,14 @@ export default function TopReferrers({ referrers, collectReferrers = true, siteI const useFavicon = faviconUrl && !faviconFailed.has(referrer) if (useFavicon) { return ( - setFaviconFailed((prev) => new Set(prev).add(referrer))} + unoptimized /> ) } diff --git a/components/sites/SiteList.tsx b/components/sites/SiteList.tsx index fefa001..4cf70c1 100644 --- a/components/sites/SiteList.tsx +++ b/components/sites/SiteList.tsx @@ -1,6 +1,7 @@ 'use client' import Link from 'next/link' +import Image from 'next/image' import { Site } from '@/lib/api/sites' import type { Stats } from '@/lib/api/stats' import { formatNumber } from '@ciphera-net/ui' @@ -34,10 +35,13 @@ function SiteCard({ site, stats, statsLoading, onDelete, canDelete }: SiteCardPr
- {site.name}
diff --git a/next.config.ts b/next.config.ts index 2bf94f0..484ecc8 100644 --- a/next.config.ts +++ b/next.config.ts @@ -12,6 +12,15 @@ const nextConfig: NextConfig = { output: 'standalone', // * Privacy-first: Disable analytics and telemetry productionBrowserSourceMaps: false, + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'www.google.com', + pathname: '/s2/favicons**', + }, + ], + }, async redirects() { return [ {