Skip to content

Instantly share code, notes, and snippets.

@rphlmr
Created July 9, 2025 09:11
Show Gist options
  • Save rphlmr/10f3c153e0d564e4ad4226a1ad3682d6 to your computer and use it in GitHub Desktop.
Save rphlmr/10f3c153e0d564e4ad4226a1ad3682d6 to your computer and use it in GitHub Desktop.
media client hints
import { type ClientHint, getHintUtils } from "@epic-web/client-hints";
import { useEffect } from "react";
import { useRevalidator } from "react-router";
const MOBILE_BREAKPOINT = "(max-width: 1023px)"; // lower than lg
const media = {
mobile: "mobile",
desktop: "desktop",
} as const;
type Device = (typeof media)[keyof typeof media];
const mediaClientHint = {
cookieName: "CH-media",
getValueCode: `window.matchMedia('${MOBILE_BREAKPOINT}').matches ? '${media.mobile}' : '${media.desktop}'`,
fallback: media.desktop,
transform(value) {
return value === media.mobile ? media.mobile : media.desktop;
},
} as const satisfies ClientHint<Device>;
const hintsUtils = getHintUtils({
media: mediaClientHint,
});
export const { getHints } = hintsUtils;
function subscribeToMediaChange(callback: () => void) {
const schemaMatch = window.matchMedia(MOBILE_BREAKPOINT);
function handleMediaChange() {
const value = schemaMatch.matches ? media.mobile : media.desktop;
document.cookie = `${mediaClientHint.cookieName}=${value}; Max-Age=31536000; SameSite=Lax; Path=/`;
callback();
}
schemaMatch.addEventListener("change", handleMediaChange);
return () => {
schemaMatch.removeEventListener("change", handleMediaChange);
};
}
export function ClientHintCheck({ nonce }: { nonce: string }) {
const { revalidate } = useRevalidator();
useEffect(() => subscribeToMediaChange(() => revalidate()), [revalidate]);
return (
<script
nonce={nonce}
dangerouslySetInnerHTML={{
__html: hintsUtils.getClientHintCheckScript(),
}}
/>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment