Skip to content

Commit

Permalink
Feat/23 web vitals (#25)
Browse files Browse the repository at this point in the history
* feat: add client side of web vitals
* feat: write web vitals to DynamoDB table
  • Loading branch information
fastner authored Aug 7, 2024
1 parent dad628f commit b0649e7
Show file tree
Hide file tree
Showing 9 changed files with 1,127 additions and 8 deletions.
57 changes: 57 additions & 0 deletions app/components/hooks/webVitals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useEffect } from "react"
import type { Metric } from "web-vitals"

export interface AnalyticsData {
page: string
href: string
metric: Metric
}

async function sendToAnalytics(metric: Metric) {
const data: AnalyticsData = {
page: window.location.pathname,
href: window.location.href,
metric
}
const body = JSON.stringify(data)

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (navigator.sendBeacon) {
navigator.sendBeacon("/analytics", body)
} else {
await fetch("/analytics", { body, method: "POST", keepalive: true })
}
}

export function useWebVitals() {
useEffect(() => {
async function handleWebVitals() {
const {
onCLS: onCls,
onINP: onInp,
onLCP: onLcp,
onFCP: onFcp,
onTTFB: onTtfb
} = await import("web-vitals")

onCls(sendToAnalytics)
onInp(sendToAnalytics)
onLcp(sendToAnalytics)
onFcp(sendToAnalytics)
onTtfb(sendToAnalytics)
}

const idleRunner = () => {
handleWebVitals().catch((error: unknown) => {
console.error("Failed to load web-vitals", error)
})
}

// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (window.requestIdleCallback) {
window.requestIdleCallback(idleRunner)
} else {
setTimeout(idleRunner, 1)
}
}, [])
}
3 changes: 3 additions & 0 deletions app/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from "@remix-run/react"
import type { PropsWithChildren } from "react"

import { useWebVitals } from "./components/hooks/webVitals"
import { Body, Favicon, Footer, Header, Main } from "./components/page"

export function Layout({ children }: PropsWithChildren) {
Expand Down Expand Up @@ -44,5 +45,7 @@ export function Layout({ children }: PropsWithChildren) {
}

export default function App() {
useWebVitals()

return <Outlet />
}
40 changes: 40 additions & 0 deletions app/routes/analytics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import awsLite from "@aws-lite/client"
import awsDynamoDB from "@aws-lite/dynamodb"
import type { ActionFunctionArgs } from "@remix-run/node"
import { Resource } from "sst"

import type { AnalyticsData } from "~/components/hooks/webVitals"

interface DatabaseAnalyticsData extends AnalyticsData {
id: string
createdAt: string
}

export async function action({ request }: ActionFunctionArgs) {
const aws = await awsLite({
region: "eu-central-1",
plugins: [awsDynamoDB]
})

const data = (await request.json()) as AnalyticsData

const entry: DatabaseAnalyticsData = {
...data,
id: data.metric.id,
createdAt: new Date().toISOString()
}

try {
// eslint-disable-next-line new-cap
await aws.DynamoDB.PutItem({
TableName: Resource.WebVitals.name,
Item: entry
})
} catch (error) {
return new Response(`Error saving data: ${error as string}`, {
status: 500
})
}

return new Response("", { status: 204 })
}
1 change: 1 addition & 0 deletions aws-lite.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
declare module "@aws-lite/dynamodb"
10 changes: 8 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,18 @@
"test:format": "prettier --check .",
"build": "remix vite:build",
"dev": "remix vite:dev",
"sst:dev": "sst dev remix vite:dev",
"start": "remix-serve ./build/server/index.js",
"opt:svg": "svgo --folder app --recursive",
"update:latest": "ncu --deep --upgrade --interactive",
"update:minor": "ncu --deep --upgrade --interactive --target minor",
"update:pnpm": "corepack up",
"deploy:prod": "sst deploy --stage production"
"deploy:prod": "sst deploy --stage production",
"deploy:dev": "sst deploy --stage development"
},
"dependencies": {
"@aws-lite/client": "^0.22.4",
"@aws-lite/dynamodb": "^0.3.8",
"@effective/shadow": "^1.1.0",
"@remix-run/node": "^2.11.0",
"@remix-run/react": "^2.11.0",
Expand All @@ -36,9 +40,11 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"remark-frontmatter": "^5.0.0",
"remark-mdx-frontmatter": "^5.0.0"
"remark-mdx-frontmatter": "^5.0.0",
"web-vitals": "^4.2.2"
},
"devDependencies": {
"@aws-lite/dynamodb-types": "^0.3.10",
"@commitlint/cli": "^19.3.0",
"@commitlint/config-conventional": "^19.2.2",
"@effective/color": "^1.0.1",
Expand Down
Loading

0 comments on commit b0649e7

Please sign in to comment.