Skip to content

Commit 02ad9e9

Browse files
authored
Polish, random movie quote, credits (#10)
* Random movie quote (force dynamic) * Content tweaks, use more Markdown
1 parent f7cdfcf commit 02ad9e9

18 files changed

+888
-805
lines changed

.eslintrc.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"extends": "next/core-web-vitals"
2+
"extends": "next/core-web-vitals",
3+
"rules": {
4+
"@next/next/no-img-element": "off"
5+
}
36
}

app/api/posts/random/route.js

-8
This file was deleted.

app/edge/explainer.jsx

+21-17
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
1-
import { promises as fs } from 'fs';
2-
import Link from 'next/link';
3-
import { CodeBlock } from '../../components/code-block';
1+
import { Markdown } from "components/markdown";
42

5-
const edgeFunctionFile = './netlify/edge-functions/rewrite.js';
3+
const explainer = `
4+
This page is using a Netlify Edge Function (\`netlify/edge-functions/rewrite.js\`) to rewrite the URL based on visitor geography.
65
7-
export default async function EdgeFunctionExplainer() {
8-
const code = (await fs.readFile(edgeFunctionFile)).toString();
9-
return (
10-
<>
11-
<p className="mb-6">This page is using a Netlify Edge Function to rewrite the URL based on visitor geography.</p>
12-
<CodeBlock lineNumbers lang="js" title={edgeFunctionFile} code={code} />
13-
<p className="mt-8">
14-
<Link href="https://edge-functions-examples.netlify.app" className="transition link hover:opacity-80">
15-
See more examples
16-
</Link>
17-
</p>
18-
</>
19-
);
6+
~~~js
7+
const rewrite = async (request, context) => {
8+
const path = context.geo?.country?.code === 'AU' ? '/edge/australia' : '/edge/not-australia';
9+
return new URL(path, request.url);
10+
};
11+
12+
export const config = {
13+
path: '/edge'
14+
};
15+
16+
export default rewrite;
17+
~~~
18+
19+
[See more examples](https://edge-functions-examples.netlify.app)
20+
`;
21+
22+
export default function EdgeFunctionExplainer() {
23+
return <Markdown content={explainer} />
2024
}

app/edge/page.jsx

+13-11
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import Link from 'next/link';
22
import { Alert } from '../../components/alert';
3+
import { Markdown } from 'components/markdown';
34

45
export const metadata = {
56
title: 'Fallback'
67
};
78

9+
const explainer = `
10+
This page is using a [Netlify Edge Function](https://docs.netlify.com/edge-functions/overview/) to rewrite the URL based on visitor geography.
11+
12+
For it to be invoked, please either run this site locally with \`netlify dev\` or deploy it to Netlify.
13+
14+
Edge Functions are framework-agnostic, but are also used behind the scenes to run Next.js Middleware on Netlify.
15+
There are advatanges to using Edge Functions directly, such as the ability to access & transform the response body.
16+
17+
[See more examples](https://edge-functions-examples.netlify.app)
18+
`
19+
820
export default function FallbackPage() {
921
return (
1022
<>
1123
<h1 className="mb-8 text-4xl font-bold tracking-tight sm:text-5xl sm:mb-12">You&apos;ve reached the fallback page.</h1>
12-
<p className="mb-6 leading-7">
13-
This page is using a Netlify Edge Function to rewrite the URL based on visitor geography.
14-
<br />
15-
For it to be invoked, please either run this site locally with <code>netlify dev</code> or deploy to Netlify!
16-
<br /><br/>
17-
Alternatively, you can use Next.js Middleware, which is also automatically deployed on Netlify as an Edge Function.<br/>
18-
However, note that Edge Functions are more powerful (e.g., you can fully transform the response) and framework-agnostic.
19-
</p>
20-
<Link href="https://edge-functions-examples.netlify.app" className="transition link hover:opacity-80">
21-
See more examples
22-
</Link>
24+
<Markdown content={explainer} />
2325
</>
2426
);
2527
}

app/image-cdn/image-with-size-overlay.jsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import { useCallback, useEffect, useRef, useState } from 'react';
4-
import { getResourceSize } from '../../utils';
4+
import { getResourceSize } from 'utils';
55

66
export function ImageWithSizeOverlay({ src, srcSet, sizes, overlayPosition }) {
77
const imageRef = useRef();
@@ -25,11 +25,13 @@ export function ImageWithSizeOverlay({ src, srcSet, sizes, overlayPosition }) {
2525
<div className="relative">
2626
{imgSize && (
2727
<span
28-
className={`absolute py-1.5 px-2.5 text-sm rounded-lg bg-neutral-900/70 top-2.5 ${overlayPosition === 'right' ? 'right-2.5' : 'left-2.5'}`}
28+
className={`absolute py-1.5 px-2.5 text-sm rounded-lg bg-neutral-900/70 top-2.5 ${
29+
overlayPosition === 'right' ? 'right-2.5' : 'left-2.5'
30+
}`}
2931
>{`Size: ${Math.ceil(imgSize / 1024)}KB`}</span>
3032
)}
31-
32-
<img src={src} srcSet={srcSet} sizes={sizes} alt="Corgi" onLoad={handleImageLoad} ref={imageRef} />
33+
34+
<img src={src} srcSet={srcSet} sizes={sizes} alt="Corgi" onLoad={handleImageLoad} ref={imageRef} />
3335
</div>
3436
);
3537
}

app/image-cdn/page.jsx

+29-16
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import { Alert } from '../../components/alert';
2-
import { getNetlifyContext } from '../../utils';
3-
import { ImageWithSizeOverlay } from './image-with-size-overlay';
4-
import { Markdown } from '../../components/markdown';
51
import Image from 'next/image';
2+
import { Alert } from 'components/alert';
3+
import { Markdown } from 'components/markdown';
4+
import { getNetlifyContext } from 'utils';
5+
import { ImageWithSizeOverlay } from './image-with-size-overlay';
66

77
export const metadata = {
88
title: 'Image CDN'
99
};
1010

11-
const sampleImage = "/images/corgi.jpg";
12-
const sampleImageSizes = "(max-width: 1024px) 100vw, 1024px";
11+
const sampleImage = '/images/corgi.jpg';
1312

1413
const ctx = getNetlifyContext();
1514
const forceWebP = ctx === 'dev';
16-
const sampleImageSrcSet = [640, 1280, 2048].map(size => {
17-
return `/.netlify/images?url=${sampleImage}&w=${size}${forceWebP ? '&fm=webp' : ''} ${size}w`
18-
}).join(', ')
15+
const sampleImageSrcSet = [640, 1280, 2048]
16+
.map((size) => {
17+
return `/.netlify/images?url=${sampleImage}&w=${size}${forceWebP ? '&fm=webp' : ''} ${size}w`;
18+
})
19+
.join(', ');
1920

2021
const nextImageSnippet = `
2122
When running on Netlify, \`next/image\` is automatically set-up to use Netlify Image CDN for optimized images.
@@ -61,7 +62,8 @@ export default function Page() {
6162
{ctx === 'dev' && (
6263
<Alert>
6364
<p>
64-
Running in local development mode. Image optimization is run locally without format detection, so format is fixed to WebP.
65+
Running in local development mode. Image optimization is run locally without format
66+
detection, so format is fixed to WebP.
6567
<br />
6668
Run this site on Netlify for automatic format detection!
6769
</p>
@@ -71,7 +73,10 @@ export default function Page() {
7173
<section className="mb-16 sm:gap-8 sm:mb-24">
7274
<h2 className="mb-6 text-2xl font-bold sm:text-3xl">Using next/image component</h2>
7375
<Markdown content={nextImageSnippet} />
74-
<div className="mt-8 overflow-hidden border-2 border-white rounded-lg relative max-w-screen-lg" style={{ aspectRatio: '3/2' }}>
76+
<div
77+
className="mt-8 overflow-hidden border-2 border-white rounded-lg relative max-w-screen-lg"
78+
style={{ aspectRatio: '3/2' }}
79+
>
7580
<Image
7681
src="/images/corgi.jpg"
7782
priority
@@ -81,10 +86,22 @@ export default function Page() {
8186
alt="Corgi"
8287
/>
8388
</div>
89+
<span className="text-sm italic">
90+
Credit: photo by{' '}
91+
<a href="https://unsplash.com/@alvannee?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
92+
Alvan Nee
93+
</a>{' '}
94+
on{' '}
95+
<a href="https://unsplash.com/photos/long-coated-white-and-brown-dog-lvFlpqEvuRM?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">
96+
Unsplash
97+
</a>
98+
</span>
8499
</section>
85100

86101
<section className="mb-16 sm:gap-8 sm:mb-24">
87-
<h2 className="mb-6 text-2xl font-bold sm:text-3xl">Original vs. optimized image: can you tell the difference?</h2>
102+
<h2 className="mb-6 text-2xl font-bold sm:text-3xl">
103+
Original vs. optimized image: can you tell the difference?
104+
</h2>
88105
<Markdown content={originalVsCdnSnippet} />
89106
<div className="diff aspect-[3/2] rounded-lg border-2 border-white mt-8">
90107
<div className="diff-item-1">
@@ -107,7 +124,3 @@ export default function Page() {
107124
</>
108125
);
109126
}
110-
111-
/*
112-
/.netlify/images?url=images/corgi.jpg?w=640 640w, /.netlify/images?url=images/corgi.jpg?w=1024 1024w, /.netlify/images?url=images/corgi.jpg?w=2048 2048w, "
113-
*/

app/page.jsx

+36-18
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,54 @@
11
import Link from 'next/link';
2-
import { Card } from '../components/card';
3-
import { CardsGrid } from '../components/cards-grid';
4-
import { RandomPostId } from '../components/random-post-id';
5-
import { getNetlifyContext } from '../utils';
6-
import { Alert } from '../components/alert';
2+
import { Card } from 'components/card';
3+
import { RandomQuote } from 'components/random-quote';
4+
import { Alert } from 'components/alert';
5+
import { Markdown } from 'components/markdown';
6+
import { getNetlifyContext } from 'utils';
77

88
const cards = [
9-
/*{
10-
text: 'Hello',
11-
linkText: 'someLink',
12-
href: '/'
13-
}*/
9+
//{ text: 'Hello', linkText: 'someLink', href: '/' }
1410
];
1511

16-
const currentEnv = process.env.NODE_ENV;
12+
const contextExplainer = `
13+
The card below is rendered on the server based on the value of \`process.env.CONTEXT\`
14+
([docs](https://docs.netlify.com/configure-builds/environment-variables/#build-metadata)):
15+
`;
16+
17+
const preDynamicContentExplainer = `
18+
The card content below is fetched by the client-side from \`/quotes/random\` (see file \`app/quotes/random/route.js\`) with a different quote shown on each page load:
19+
`;
20+
21+
const postDynamicContentExplainer = `
22+
On Netlify, Next.js Route Handlers are automatically deployed as [Serverless Functions](https://docs.netlify.com/functions/overview/).
23+
Alternatively, you can add Serverless Functions to any site regardless of framework, with acccess to the [full context data](https://docs.netlify.com/functions/api/).
24+
25+
And as always with dynamic content, beware of layout shifts & flicker! (here, we aren't...)
26+
`;
1727

1828
export default function Page() {
1929
return (
20-
<>
21-
<section className="flex flex-col items-start gap-6 mb-16 sm:gap-8 sm:mb-24">
30+
<main className="flex flex-col gap-8 sm:gap-16">
31+
<section className="flex flex-col items-start gap-4 sm:gap-6">
2232
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl">Netlify Platform Starter - Next.js</h1>
2333
<p className="text-lg">Get started with Next.js and Netlify in seconds.</p>
24-
<Link href="https://docs.netlify.com/frameworks/next-js/overview/" className="btn btn-lg btn-primary sm:btn-wide">
34+
<Link
35+
href="https://docs.netlify.com/frameworks/next-js/overview/"
36+
className="btn btn-lg btn-primary sm:btn-wide"
37+
>
2538
Read the Docs
2639
</Link>
2740
</section>
28-
<section className="flex flex-col gap-8">
41+
<section className="flex flex-col gap-4">
42+
<Markdown content={contextExplainer} />
2943
<RuntimeContextCard />
30-
{!!cards?.length && <CardsGrid cards={cards} />}
31-
<RandomPostId />
3244
</section>
33-
</>
45+
<section className="flex flex-col gap-4">
46+
<Markdown content={preDynamicContentExplainer} />
47+
<RandomQuote />
48+
<Markdown content={postDynamicContentExplainer} />
49+
</section>
50+
{ /* !!cards?.length && <CardsGrid cards={cards} /> */}
51+
</main>
3452
);
3553
}
3654

app/quotes/random/route.js

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { NextResponse } from 'next/server';
2+
import data from 'data/quotes.json';
3+
4+
export const dynamic = 'force-dynamic'; // Otherwise, Next.js will cache this handler's output
5+
6+
const dataSource = 'https://en.wikipedia.org/wiki/AFI%27s_100_Years...100_Movie_Quotes';
7+
8+
export async function GET() {
9+
const randomId = Math.floor(Math.random() * data.length);
10+
const item = data[randomId];
11+
12+
return NextResponse.json({
13+
...item,
14+
dataSource
15+
});
16+
}

components/geo.jsx

-35
This file was deleted.

components/header.jsx

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import Image from 'next/image';
22
import Link from 'next/link';
3-
import netlifyLogo from '../public/netlify-logo.svg';
3+
import netlifyLogo from 'public/netlify-logo.svg';
44

55
const navItems = [
66
{ linkText: 'Home', href: '/' },
77
{ linkText: 'Revalidation', href: '/revalidation' },
88
{ linkText: 'Image CDN', href: '/image-cdn' },
99
{ linkText: 'Edge Function', href: '/edge' },
10-
{ linkText: 'Blobs (TBD)', href: '/blobs' }
10+
{ linkText: 'Blobs', href: '/blobs' }
1111
];
1212

1313
export function Header() {
@@ -20,7 +20,10 @@ export function Header() {
2020
<ul className="flex flex-wrap gap-x-4 gap-y-1">
2121
{navItems.map((item, index) => (
2222
<li key={index}>
23-
<Link href={item.href} className="inline-block px-1.5 py-1 transition hover:opacity-80 sm:px-3 sm:py-2">
23+
<Link
24+
href={item.href}
25+
className="inline-block px-1.5 py-1 transition hover:opacity-80 sm:px-3 sm:py-2"
26+
>
2427
{item.linkText}
2528
</Link>
2629
</li>
@@ -29,4 +32,4 @@ export function Header() {
2932
)}
3033
</nav>
3134
);
32-
};
35+
}

0 commit comments

Comments
 (0)