From 86a19c222cb86276a49ad20fbb81b6e9ef50cf9e Mon Sep 17 00:00:00 2001 From: Barsnes Date: Fri, 24 Apr 2026 13:36:41 +0200 Subject: [PATCH 1/4] chore: consent banner check on server Co-authored-by: Copilot --- .../www/app/_hooks/use-show-consent-banner.ts | 32 ------------------- apps/www/app/layouts/root/layout.tsx | 14 +++++--- 2 files changed, 9 insertions(+), 37 deletions(-) delete mode 100644 apps/www/app/_hooks/use-show-consent-banner.ts diff --git a/apps/www/app/_hooks/use-show-consent-banner.ts b/apps/www/app/_hooks/use-show-consent-banner.ts deleted file mode 100644 index 737106606a..0000000000 --- a/apps/www/app/_hooks/use-show-consent-banner.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useEffect, useState } from 'react'; -import { hasConsent } from '~/_utils/consent.client'; - -export function useShowConsentBanner() { - const [showBanner, setShowBanner] = useState(false); - - useEffect(() => { - // Check if user already has consent - const checkConsent = async () => { - if (!(await hasConsent())) { - setShowBanner(true); - } - }; - - checkConsent(); - - // Listen for consent changes (in case user updates consent in another tab) - const handleStorageChange = async () => { - if (!(await hasConsent())) { - setShowBanner(true); - } - }; - - window.cookieStore?.addEventListener('change', handleStorageChange); - return () => - window.cookieStore?.removeEventListener('change', handleStorageChange); - }, []); - - const hideBanner = () => setShowBanner(false); - - return { showBanner, hideBanner }; -} diff --git a/apps/www/app/layouts/root/layout.tsx b/apps/www/app/layouts/root/layout.tsx index dbff89347f..55ae49fc9d 100644 --- a/apps/www/app/layouts/root/layout.tsx +++ b/apps/www/app/layouts/root/layout.tsx @@ -11,17 +11,22 @@ import { Figma } from '~/_components/logos/figma'; import { Github } from '~/_components/logos/github'; import { Slack } from '~/_components/logos/slack'; import { SearchDialog } from '~/_components/search-dialog'; -import { useShowConsentBanner } from '~/_hooks/use-show-consent-banner'; +import { CONSENT_VERSION, userConsent } from '~/_utils/cookies'; import i18n from '~/i18n'; import type { Route as RootRoute } from './../../+types/root'; import type { Route } from './+types/layout'; -export const loader = ({ params }: Route.LoaderArgs) => { +export const loader = async ({ params, request }: Route.LoaderArgs) => { if (!i18n.supportedLngs.includes(params.lang || '')) { throw new Response('Not Found', { status: 404, }); } + + const consent = await userConsent.parse(request.headers.get('Cookie')); + const showConsentBanner = !consent || consent.version !== CONSENT_VERSION; + + return { showConsentBanner }; }; const rightLinks: FooterLinkListItemProps[] = [ @@ -49,7 +54,7 @@ const rightLinks: FooterLinkListItemProps[] = [ }, ]; -export default function RootLayout() { +export default function RootLayout({ loaderData }: Route.ComponentProps) { const { t } = useTranslation(); const { lang, centerLinks, menu } = useRouteLoaderData('root') as Omit< RootRoute.ComponentProps['loaderData'], @@ -61,7 +66,6 @@ export default function RootLayout() { href: string; }[]; }; - const { showBanner } = useShowConsentBanner(); useChangeLanguage(lang); @@ -72,7 +76,7 @@ export default function RootLayout() { return ( <>
- {showBanner && } + {loaderData?.showConsentBanner && } {t('accessibility.skip-link')}
Date: Fri, 24 Apr 2026 14:12:37 +0200 Subject: [PATCH 2/4] Update apps/www/app/layouts/root/layout.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- apps/www/app/layouts/root/layout.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/www/app/layouts/root/layout.tsx b/apps/www/app/layouts/root/layout.tsx index 55ae49fc9d..b1fe1722c9 100644 --- a/apps/www/app/layouts/root/layout.tsx +++ b/apps/www/app/layouts/root/layout.tsx @@ -23,7 +23,12 @@ export const loader = async ({ params, request }: Route.LoaderArgs) => { }); } - const consent = await userConsent.parse(request.headers.get('Cookie')); + let consent = null; + try { + consent = await userConsent.parse(request.headers.get('Cookie')); + } catch { + consent = null; + } const showConsentBanner = !consent || consent.version !== CONSENT_VERSION; return { showConsentBanner }; From 9d852fab4d5861d9733507b9ff09f2224ab07b0f Mon Sep 17 00:00:00 2001 From: Barsnes Date: Fri, 24 Apr 2026 14:22:20 +0200 Subject: [PATCH 3/4] `clientLoader` Co-authored-by: Copilot --- apps/www/app/layouts/root/layout.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/www/app/layouts/root/layout.tsx b/apps/www/app/layouts/root/layout.tsx index b1fe1722c9..cf499bb4ff 100644 --- a/apps/www/app/layouts/root/layout.tsx +++ b/apps/www/app/layouts/root/layout.tsx @@ -11,6 +11,7 @@ import { Figma } from '~/_components/logos/figma'; import { Github } from '~/_components/logos/github'; import { Slack } from '~/_components/logos/slack'; import { SearchDialog } from '~/_components/search-dialog'; +import { hasConsent } from '~/_utils/consent.client'; import { CONSENT_VERSION, userConsent } from '~/_utils/cookies'; import i18n from '~/i18n'; import type { Route as RootRoute } from './../../+types/root'; @@ -34,6 +35,12 @@ export const loader = async ({ params, request }: Route.LoaderArgs) => { return { showConsentBanner }; }; +export const clientLoader = async () => { + const showConsentBanner = !(await hasConsent()); + return { showConsentBanner }; +}; +clientLoader.hydrate = true as const; + const rightLinks: FooterLinkListItemProps[] = [ { text: 'designsystem@digdir.no' as unknown as FooterLinkListItemProps['text'], From 800d97c13379713ede28d1babe145d9588423456 Mon Sep 17 00:00:00 2001 From: Barsnes Date: Fri, 24 Apr 2026 14:35:09 +0200 Subject: [PATCH 4/4] update code Co-authored-by: Copilot --- apps/www/app/layouts/root/layout.tsx | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/apps/www/app/layouts/root/layout.tsx b/apps/www/app/layouts/root/layout.tsx index cf499bb4ff..067a1ce4ab 100644 --- a/apps/www/app/layouts/root/layout.tsx +++ b/apps/www/app/layouts/root/layout.tsx @@ -12,29 +12,9 @@ import { Github } from '~/_components/logos/github'; import { Slack } from '~/_components/logos/slack'; import { SearchDialog } from '~/_components/search-dialog'; import { hasConsent } from '~/_utils/consent.client'; -import { CONSENT_VERSION, userConsent } from '~/_utils/cookies'; -import i18n from '~/i18n'; import type { Route as RootRoute } from './../../+types/root'; import type { Route } from './+types/layout'; -export const loader = async ({ params, request }: Route.LoaderArgs) => { - if (!i18n.supportedLngs.includes(params.lang || '')) { - throw new Response('Not Found', { - status: 404, - }); - } - - let consent = null; - try { - consent = await userConsent.parse(request.headers.get('Cookie')); - } catch { - consent = null; - } - const showConsentBanner = !consent || consent.version !== CONSENT_VERSION; - - return { showConsentBanner }; -}; - export const clientLoader = async () => { const showConsentBanner = !(await hasConsent()); return { showConsentBanner }; @@ -88,7 +68,9 @@ export default function RootLayout({ loaderData }: Route.ComponentProps) { return ( <>
- {loaderData?.showConsentBanner && } + {loaderData?.showConsentBanner || false ? ( + + ) : null} {t('accessibility.skip-link')}