diff --git a/app/api/og-image/route.tsx b/app/api/og-image/route.tsx
new file mode 100644
index 0000000..1bd4cbe
--- /dev/null
+++ b/app/api/og-image/route.tsx
@@ -0,0 +1,63 @@
+import {ImageResponse} from 'next/og'
+
+export const runtime = 'edge'
+
+const font = fetch(new URL('/assets/Inter.ttf', import.meta.url)).then(
+ (res) => res.arrayBuffer()
+)
+
+const gradients = [
+ 'radial-gradient(at right top, rgb(221, 214, 254), rgb(239, 68, 68), rgb(251, 146, 60))',
+ 'radial-gradient(at right bottom, rgb(255, 255, 255), rgb(244, 114, 182), rgb(240, 171, 252))',
+ 'radial-gradient(at left bottom, rgb(254, 202, 202), rgb(8, 145, 178), rgb(236, 252, 203))',
+ 'radial-gradient(at left top, rgb(124, 58, 237), rgb(220, 252, 231), rgb(31, 41, 55))',
+ 'radial-gradient(at left bottom, rgb(214, 211, 209), rgb(190, 242, 100), rgb(190, 242, 100))'
+]
+
+export async function GET(request: Request) {
+ const fontData = await font
+ const {searchParams} = new URL(request.url)
+ const title = searchParams.get('title')
+
+ return new ImageResponse(
+ (
+
+ ),
+ {
+ width: 1200,
+ height: 600,
+ fonts: [
+ {
+ name: 'Inter',
+ data: fontData,
+ style: 'normal'
+ }
+ ]
+ }
+ );
+}
\ No newline at end of file
diff --git a/app/layout.tsx b/app/layout.tsx
index 4e773bd..5f51b5d 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -7,7 +7,11 @@ import '@/styles/tailwind.css'
export const metadata = {
title: 'Ryan Freeman - Full-stack software engineer from Dublin, Ireland.',
- description: 'Full-stack software engineer who enjoys building cloud-native applications.'
+ description: 'Full-stack software engineer who enjoys building cloud-native applications.',
+ metadataBase: new URL('https://ryanfreeman.dev'),
+ alternates: {
+ canonical: '/'
+ }
}
export default function RootLayout({children}: { children: ReactNode }) {
diff --git a/app/services/page.tsx b/app/services/page.tsx
index 3d90d4a..3edba97 100644
--- a/app/services/page.tsx
+++ b/app/services/page.tsx
@@ -10,10 +10,28 @@ import {EmailIcon} from '@/components/icons/EmailIcon'
import {RocketIcon} from '@/components/icons/RocketIcon'
import {ShoppingBagIcon} from '@/components/icons/ShoppingBagIcon'
-export const metadata = {
+const meta = {
title: 'Services - Ryan Freeman',
+ heading: 'I offer a wide range of digital services to elevate and transform your business',
description: 'Whether you need a WordPress website, React app, AWS support or odd coding jobs, I\'m here to help. ' +
- 'As an experienced software engineer, I produce high-quality software that will deliver immediate value for you and your customers.'
+ 'As an experienced software engineer, I produce high-quality software that will deliver immediate value for you and your customers.',
+}
+
+export const metadata = {
+ ...meta,
+ openGraph: {
+ title: meta.title,
+ description: meta.description,
+ images: [
+ {
+ url: `/api/og-image?title=${meta.heading}`,
+ width: 1200,
+ height: 600,
+ alt: meta.heading,
+ type: 'image/png'
+ }
+ ]
+ }
}
type Services = {
diff --git a/assets/Inter.ttf b/assets/Inter.ttf
new file mode 100644
index 0000000..053185e
Binary files /dev/null and b/assets/Inter.ttf differ
diff --git a/components/icons/CloudIcon.tsx b/components/icons/CloudIcon.tsx
index 30f095d..be59d4a 100644
--- a/components/icons/CloudIcon.tsx
+++ b/components/icons/CloudIcon.tsx
@@ -4,7 +4,7 @@ export function CloudIcon(props: Props) {
return (
)