Gatsby plugin for incremental migration to Next.js by polyfilling Next.js APIs.
- Polyfills
next/head
,next/link
,next/router
andnext/script
. - Adds support for Next.js routing.
- Data fetching via
getStaticPaths
andgetStaticProps
APIs. - Exposes TypeScript types that match Next.js ones.
- Migrate to Next.js APIs page by page without blocking other changes to your website.
$ npm install --save-dev gatsby-plugin-next
Add gatsby-plugin-next
to gatsby-config.js
:
module.exports = {
plugins: [
+ 'gatsby-plugin-next'
]
};
Append elements to <head>
of the page.
How to migrate:
// Before
import { Helmet } from "react-helmet";
export default function MyPage() {
return (
<Helmet>
<title>This is my website</title>
</Helmet>
);
}
// After
import Head from "next/head";
export default function MyPage() {
return (
<Head>
<title>This is my website</title>
</Head>
);
}
Learn more in Next.js documentation.
Navigate between pages without triggering a full page reload.
Note: This component doesn't prefetch the next page on hover, because it doesn't use Gatsby's Link
component and Gatsby doesn't expose an API to trigger prefetching.
Type: string
or UrlObject
.
import Link from "next/link";
export default function MyPage() {
return (
<Link href="/my/other/page">
<a>Go to my other page</a>
</Link>
);
}
Type: boolean
Default: false
Replace the current history state instead of adding a new url into the stack.
import Link from "next/link";
export default function MyPage() {
return (
<Link href="/my/other/page" replace>
<a>Replace this page with the other one</a>
</Link>
);
}
How to migrate:
// Before
import { Link } from "gatsby";
export default function MyPage() {
return <Link to="/my/other/page">Go to my other page</Link>;
}
// After
import Link from "next/link";
export default function MyPage() {
return <Link href="/my/other/page">
<a>Go to my other page</a>
</Link>;
}
Learn more in Next.js documentation.
React hook that returns a router
object.
import { useRouter } from "next/router";
export default function MyPage() {
const router = useRouter();
return <button onClick={() => router.push("/other/page")}>Push me</button>;
}
How to migrate:
// Before
import { navigate } from "gatsby";
import { useLocation } from "@reach/router";
export default function MyPage() {
const location = useLocation();
// Get current pathname
const pathname = location.pathname;
// Get query object
const query = new URLSearchParams(location.search);
// Navigate programmatically
const goToOtherPage = () => {
navigate("/other/page");
};
// Navigate programmatically without adding a new history entry
const replaceWithOtherPage = () => {
navigate("/other/page", { replace: true });
};
return <SomeComponents />;
};
// After
import { useRouter } from "next/router";
export default function MyPage() {
const router = useRouter();
// Get current pathname
const pathname = router.asPath;
// Get query object
const query = router.query;
// Navigate programmatically
const goToOtherPage = () => {
router.push("/other/page");
};
// Navigate programmatically without adding a new history entry
const replaceWithOtherPage = () => {
router.replace("/other/page");
};
return <SomeComponents />;
};
Learn more in Next.js documentation.
The path (including the query and a hash) shown in the browser.
const router = useRouter();
router.asPath; //=> /my/page
The query string parsed to an object.
const router = useRouter();
router.query; //=> { "queryParameter": "hello" }
Navigate to a page.
Type: string
or UrlObject
.
const router = useRouter();
// String
router.push("/my/page");
// URL object
router.push({
pathname: "/my/page",
});
Navigate to page without adding a new URL entry into the history
stack.
API is the same as push
.
Navigate back in history.
const router = useRouter();
router.back();
Reload the current URL.
const router = useRouter();
router.reload();
Learn more in Next.js documentation.
Returns Higher-Order-Component that injects a router
object as a prop.
import React from "react";
import { withRouter } from "next/router";
class MyPage extends React.Component {
render() {
const { router } = this.props;
return <p>{router.pathname}</p>;
}
}
export default withRouter(MyPage);
Learn more in Next.js documentation.
next/router
exposes NextRouter
type in case you need to use withRouter
in a TypeScript codebase.
import { withRouter, NextRouter } from "next/router";
interface WithRouterProps {
router: NextRouter;
}
interface MyComponentProps extends WithRouterProps {}
class MyPage extends React.Component<MyComponentProps> {
render() {
const { router } = this.props;
return <p>{router.pathname}</p>;
}
}
export default withRouter(MyPage);
Learn more in Next.js documentation.
Load external scripts that aren't compiled as part of your bundle.
Type: string
A path string specifying the URL of an external script. This can be either an absolute external URL or an internal path.
Type: string
Default: afterInteractive
The loading strategy of the script.
beforeInteractive
- Load script before the page becomes interactive by injecting it into<head>
during SSR.afterInteractive
- Load script immediately after the page becomes interactive.lazyOnload
- Load script during browser idle time.
Type: function
Callback, which is executed after script is loaded.
Note: It can't be used with beforeInteractive
strategy.
Type: function
Callback, which is executed when script has failed to load.
Note: It can't be used with beforeInteractive
strategy.
import Script from "next/script";
export default function MyPage() {
return (
<>
<Script src="/external-script.js" />
<Script
src="https://plausible.io/js/script.js"
data-domain="mydomain.com"
/>
</>
);
}
Learn more in Next.js documentation.
This plugin lets you incrementally migrate data fetching from Gatsby APIs to Next.js ones.
All pages in src/next-pages
directory (you need to create it yourself) can use Next.js APIs like getStaticProps
to fetch data, while pages in src/pages
will continue to fetch data via Gatsby's GraphQL layer.
Files in src/next-pages
use Next.js-style routing, like [id].js
, with support for nested routes like deeply/nested/posts/[id].js
.
Each page in src/next-pages
requires two files:
posts.js
- Exports a React component to render the page.posts.data.js
- ExportsgetStaticProps
to fetch the data, which will be passed to component exported fromposts.js
.
For example:
posts.js
import React from "react";
import Link from "next/link";
export default function Posts({ pageContext: { posts } }) {
return (
<ul>
{posts.map((post) => (
<li key={post.id}>
<Link href={`/posts/${post.id}`}>
<a>{post.title}</a>
</Link>
</li>
))}
</ul>
);
}
Note: When you will switch to the actual Next.js, don't forget to remove pageContext
, as this is a variable specific to Gatsby.
-export default function Posts({ pageContext: { posts } }) {
+export default function Posts({ posts }) {
posts.data.js
import fetch from "node-fetch";
export async function getStaticProps() {
const response = await fetch("https://my-headless-cms.com/posts");
const posts = await response.json();
//=> [{ id: 1, title: "Post title", body: "..." }, ...]
return {
props: { posts },
};
}
With the files above, Gatsby will generate /posts
page with a list of posts from getStaticProps
function. You can fetch data in any way you need, since it's just a regular function executed at build-time.
Now we need to add a page for displaying each post by creating a /posts/[id]
route:
src/next-pages/
- posts.js
- posts.data.js
+ - posts/
+ - [id].js
+ - [id].data.js
[id].js
import React from "react";
export default function Post({ pageContext: { post } }) {
return (
<div>
<h1>{post.title}</h1>
<p>{post.body}</p>
</div>
);
}
Since we need to generate pages for all posts at build-time, we can use getStaticPaths
function for providing a list of parameters to generate this page for.
Then, getStaticProps
function will be executed for each set of parameters and generate a separate page. For example, /posts/1
, /posts/2
and so on.
[id.data.js]
import fetch from "node-fetch";
export async function getStaticPaths() {
const response = await fetch("https://my-headless-cms.com/posts");
const posts = await response.json();
return {
paths: posts.map((post) => ({
params: {
id: post.id,
},
})),
};
}
export async function getStaticProps({ params }) {
const response = await fetch(
`https://my-headless-cms.com/posts/${params.id}`,
);
const post = await response.json();
return {
props: { post },
};
}
Done!
When you've migrated all data fetching to these APIs and you're ready to switch to Next.js:
- Move all files in
src/next-pages
intopages
directory used by Next.js (src/pages
is also supported). - Move all code from
*.data.js
files into pages themselves. - Delete
src/next-pages
.
Congratulations, you're running Next.js now.
Define this function when page has dynamic segments (e.g. [id].js
) and return a list of parameters for each page.
export async function getStaticPaths() {
return {
paths: [
{
params: { id: 1 },
},
{
params: { id: 2 },
},
// and so on
],
};
}
Note: Only paths
is used by this plugin, fallback
is ignored.
Learn more in Next.js documentation.
Define this function to fetch data and return props
object to pass to the component rendering this page.
Type: object
Context object which includes params
object returned from getStaticPaths
for this page.
export async function getStaticProps({ params }) {
const post = await fetchPostById();
return {
props: { post },
};
}
Learn more in Next.js documentation.
Note: Only props
is used by this plugin, other keys supported in the actual getStaticPaths
in Next.js are ignored.