Skip to content
This repository was archived by the owner on May 10, 2021. It is now read-only.

Commit 6a6c7ed

Browse files
committed
Add support for dynamically routed SSG pages without fallback
Copy pre-rendered, dynamically routed pages with SSG to Netlify publish directory and copy SSG page JSON data to _next/data/ directory. Makes sure that the page source is not treated as an SSR page. Otherwise, a Netlify Function would be created and they page would have a (server-side-rendered) fallback. See: #7
1 parent 89295e8 commit 6a6c7ed

File tree

7 files changed

+163
-2
lines changed

7 files changed

+163
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Link from 'next/link'
2+
3+
const Show = ({ show }) => (
4+
<div>
5+
<p>
6+
This page uses getStaticProps() to pre-fetch a TV show.
7+
</p>
8+
9+
<hr/>
10+
11+
<h1>Show #{show.id}</h1>
12+
<p>
13+
{show.name}
14+
</p>
15+
16+
<hr/>
17+
18+
<Link href="/">
19+
<a>Go back home</a>
20+
</Link>
21+
</div>
22+
)
23+
24+
export async function getStaticPaths() {
25+
// Set the paths we want to pre-render
26+
const paths = [
27+
{ params: { id: '1' } },
28+
{ params: { id: '2' } }
29+
]
30+
31+
// We'll pre-render only these paths at build time.
32+
// { fallback: false } means other routes should 404.
33+
return { paths, fallback: false }
34+
}
35+
36+
37+
export async function getStaticProps({ params }) {
38+
// The ID to render
39+
const { id } = params
40+
41+
const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
42+
const data = await res.json();
43+
44+
return {
45+
props: {
46+
show: data
47+
}
48+
}
49+
}
50+
51+
export default Show

cypress/fixtures/pages/index.js

+10
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ const Index = ({ shows }) => (
100100
<a>getStaticProps/static</a>
101101
</Link>
102102
</li>
103+
<li>
104+
<Link href="/getStaticProps/1">
105+
<a>getStaticProps/1 (dynamic route)</a>
106+
</Link>
107+
</li>
108+
<li>
109+
<Link href="/getStaticProps/2">
110+
<a>getStaticProps/2 (dynamic route)</a>
111+
</Link>
112+
</li>
103113
</ul>
104114

105115
<h1>5. Static Pages Stay Static</h1>

cypress/integration/default_spec.js

+43
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,49 @@ describe('getStaticProps', () => {
144144
cy.get('p').should('contain', 'Dancing with the Stars')
145145
})
146146
})
147+
148+
context('with dynamic route and no fallback', () => {
149+
it('loads shows 1 and 2', () => {
150+
cy.visit('/getStaticProps/1')
151+
cy.get('h1').should('contain', 'Show #1')
152+
cy.get('p').should('contain', 'Under the Dome')
153+
154+
cy.visit('/getStaticProps/2')
155+
cy.get('h1').should('contain', 'Show #2')
156+
cy.get('p').should('contain', 'Person of Interest')
157+
})
158+
159+
it('loads page props from data .json file when navigating to it', () => {
160+
cy.visit('/')
161+
cy.window().then(w => w.noReload = true)
162+
163+
// Navigate to page and test that no reload is performed
164+
// See: https://glebbahmutov.com/blog/detect-page-reload/
165+
cy.contains('getStaticProps/1').click()
166+
cy.window().should('have.property', 'noReload', true)
167+
168+
cy.get('h1').should('contain', 'Show #1')
169+
cy.get('p').should('contain', 'Under the Dome')
170+
171+
cy.contains('Go back home').click()
172+
cy.contains('getStaticProps/2').click()
173+
174+
cy.get('h1').should('contain', 'Show #2')
175+
cy.get('p').should('contain', 'Person of Interest')
176+
})
177+
178+
it('returns 404 when trying to access non-defined path', () => {
179+
cy.request({
180+
url: '/getStaticProps/3',
181+
failOnStatusCode: false
182+
}).then(response => {
183+
expect(response.status).to.eq(404)
184+
cy.state('document').write(response.body)
185+
})
186+
187+
cy.get('h2').should('contain', 'This page could not be found.')
188+
})
189+
})
147190
})
148191

149192
describe('API endpoint', () => {

lib/allNextJsPages.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const getAllPages = () => {
1212
)
1313

1414
// Read prerender manifest that tells us which SSG pages exist
15-
const { routes: ssgPages } = readJSONSync(
15+
const { routes: ssgPages, dynamicRoutes: dynamicSsgPages } = readJSONSync(
1616
join(NEXT_DIST_DIR, "prerender-manifest.json")
1717
)
1818

@@ -23,7 +23,7 @@ const getAllPages = () => {
2323
return
2424

2525
// Skip page if it is actually an SSG page
26-
if(route in ssgPages)
26+
if(route in ssgPages || route in dynamicSsgPages)
2727
return
2828

2929
// Check if we already have a page pointing to this file

tests/__snapshots__/defaults.test.js.snap

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ exports[`Routing creates Netlify redirects 1`] = `
88
/static /static.html 200
99
/404 /404.html 200
1010
/getStaticProps/static /getStaticProps/static.html 200
11+
/getStaticProps/1 /getStaticProps/1.html 200
12+
/getStaticProps/2 /getStaticProps/2.html 200
1113
/api/shows/:id /.netlify/functions/next_api_shows_id 200
1214
/api/shows/* /.netlify/functions/next_api_shows_params 200
1315
/shows/:id /.netlify/functions/next_shows_id 200

tests/defaults.test.js

+4
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ describe('SSG Pages with getStaticProps', () => {
9898
const OUTPUT_PATH = join(PROJECT_PATH, "out_publish")
9999

100100
expect(existsSync(join(OUTPUT_PATH, "getStaticProps", "static.html"))).toBe(true)
101+
expect(existsSync(join(OUTPUT_PATH, "getStaticProps", "1.html"))).toBe(true)
102+
expect(existsSync(join(OUTPUT_PATH, "getStaticProps", "2.html"))).toBe(true)
101103
})
102104

103105
test('creates data .json file in /_next/data/ directory', () => {
@@ -107,6 +109,8 @@ describe('SSG Pages with getStaticProps', () => {
107109
const dataDir = join(PROJECT_PATH, "out_publish", "_next", "data", dirs[0])
108110

109111
expect(existsSync(join(dataDir, "getStaticProps", "static.json"))).toBe(true)
112+
expect(existsSync(join(dataDir, "getStaticProps", "1.json"))).toBe(true)
113+
expect(existsSync(join(dataDir, "getStaticProps", "2.json"))).toBe(true)
110114
})
111115
})
112116

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import Link from 'next/link'
2+
3+
const Show = ({ show }) => (
4+
<div>
5+
<p>
6+
This page uses getStaticProps() to pre-fetch a TV show.
7+
</p>
8+
9+
<hr/>
10+
11+
<h1>Show #{show.id}</h1>
12+
<p>
13+
{show.name}
14+
</p>
15+
16+
<hr/>
17+
18+
<Link href="/">
19+
<a>Go back home</a>
20+
</Link>
21+
</div>
22+
)
23+
24+
export async function getStaticPaths() {
25+
// Set the paths we want to pre-render
26+
const paths = [
27+
{ params: { id: '1' } },
28+
{ params: { id: '2' } }
29+
]
30+
31+
// We'll pre-render only these paths at build time.
32+
// { fallback: false } means other routes should 404.
33+
return { paths, fallback: false }
34+
}
35+
36+
37+
export async function getStaticProps({ params }) {
38+
// The ID to render
39+
const { id } = params
40+
41+
const res = await fetch(`https://api.tvmaze.com/shows/${id}`);
42+
const data = await res.json();
43+
44+
return {
45+
props: {
46+
show: data
47+
}
48+
}
49+
}
50+
51+
export default Show

0 commit comments

Comments
 (0)