next-data-hooks: static props as React hooks in Next.js
Use `getStaticProps`/`getServerSideProps` as react-hooks
At a glance
- What is it?
- next-data-hooks is an MIT licensed library that lets you write Next.js data queries as React hooks, by lifting getStaticProps and getServerSideProps into React context, with a TypeScript type guard for the server side context and a Babel plugin that strips the server code from the client bundle. The judgement is that it is a pattern rather than a capability, and that the readme is unusually honest about that distinction.
- Who is it for?
- next-data-hooks fits a Next.js codebase whose data fetching has grown into one large query per page, because the library lets each component own its own query while the server side props are still fetched once, on the server, as Next.js requires.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Static props, lifted into context
next-data-hooks is described as a small and simple library that lets you write React hooks for data queries in Next.js by lifting static props into React Context.
The problem it addresses is familiar to anyone who has grown a Next.js application. The framework gives you getStaticProps and getServerSideProps, and both of them have to live on a page, so a project ends up with one large query per page, a function that fetches everything the page might need, and a component tree that receives one big props object.
The library's pitch is that this does not organise well, and that the frameworks which solved it for API data, Apollo, Relay and React Query, all work by letting you write the query next to the component that needs it. Why cannot static data queries be written next to the component too? That is the whole idea, and the mechanism is that the query becomes a hook, the hook is declared in a dataHooks static property on the component, and the server side props are hoisted into a context provider that the hook reads.
The readme is careful about what the library is, and the section is called why not. The primary thing this library offers is a pattern for organising getStaticProps and getServerSideProps, and it does not offer any more capabilities than vanilla Next.js. The readme links to an issue asking why the data hooks are not parameterised, which is the question you would ask if you wanted to use the same hook for a different set of arguments, and the fact that it is an open issue rather than a feature tells you where the design stopped.
The project is MIT licensed, its primary language is TypeScript, and it is by Rico Kahler. The release history is old, version 0.4.0 on 2021-01-02, 0.5.0 on 2021-01-04 and 0.5.1 on 2021-11-12, with the manifest at 0.5.2 and the last push to the repository on 2026-10-18.
Three steps of setup, and the one that is easy to forget
The installation is short, and one of the steps is marked with a warning in the readme.
The package comes from npm or yarn, in the ordinary way. Then a Babel plugin has to be added, because the library ships a Babel transform and the next step depends on it. At the root of the project you add a .babelrc file:
{
"presets": ["next/babel"],
"plugins": ["next-data-hooks/babel"]
}The readme says not to forget this step, and explains why: it enables code elimination to eliminate server-side code in client code. That is the point of the whole arrangement. A data hook contains an async function that fetches data, and that function must not end up in the browser bundle, because it may reference server-only code. The Babel plugin is what removes it, and without the plugin the server code ships to the client.
The third step is the provider, which goes into the application file, _app.tsx or _app.js:
import { AppProps } from 'next/app';
import { NextDataHooksProvider } from 'next-data-hooks';
function App({ Component, pageProps }: AppProps) {
const { children, ...rest } = pageProps;
return (
<NextDataHooksProvider {...rest}>
<Component {...rest}>{children}</Component>
</NextDataHooksProvider>
);
}The provider is what puts the fetched data into context, and the rest of the page props are spread into it, so the provider sees everything the server sent. Without it, the hooks have nothing to read.
A data hook is a named async function with a type guard
The hook itself is created with a name and an async function, and the context it receives is the one Next.js passes to the data fetching functions.
import { createDataHook } from 'next-data-hooks';
// this context is the GetStaticPropsContext from 'next'
const useBlogPost = createDataHook('BlogPost', async (context) => {
const slug = context.params?.slug as string;
// do something async to grab the data your component needs
const blogPost = /* ... */;
return blogPost;
});The name is what identifies the data in the props, and the function returns the data the component will receive. Because the function is written where the component is, the query sits next to the thing that uses it, which is the organisational goal.
The TypeScript part is the second of the two reasons the readme gives for the library. When you import a data hook, you are also importing its return type, so when you call the hook inside your component the types are already there. There is one wrinkle, which is that the context object has a different shape in getStaticProps and in getServerSideProps, and the library ships a type guard for it:
import { createDataHook, isServerSidePropsContext } from 'next-data-hooks';
const useServerSideData = createDataHook('Data', async (context) => {
if (!isServerSidePropsContext(context)) {
throw new Error('This data hook only works in getServerSideProps.');
}
const query = context.req.query;
});So a hook that is only valid on the server side can check, at runtime and at compile time, that it is being used in the right place.
Composing hooks through a static property
The composition mechanism is a static property on the component, and it is worth understanding because it is the part that replaces the one big query.
A component that uses a data hook declares it in a static array called dataHooks, and the array can spread the data hooks of components it renders:
BlogPostComponent.dataHooks = [
...ComponentThatUsesDataHooks.dataHooks,
useOtherDataHooks,
useBlogPost,
];So the set of data a page needs is the union of the data the components need, and the components own their own requirements. A component deep in the tree declares a query, the parent collects it, and the page passes the collected set to the server side function.
The server side call is the counterpart:
export const getStaticProps: GetStaticProps = async (context) => {
const dataHooksProps = await getDataHooksProps({
context,
// this is an array of all data hooks from the `dataHooks` static prop.
dataHooks: BlogPostComponent.dataHooks,
});
return {
props: {
...dataHooksProps,
// add additional props to Next.js here
},
};
};The getDataHooksProps helper takes the context and the array of hooks, runs them, and returns the props to spread. The readme notes that this works the same way in getServerSideProps, so the pattern is not tied to static generation.
The result is that a component can be written as if it fetched its own data, the fetch still happens on the server exactly once per page, and the type of the data travels with the hook.
The routes directory trick, and why file based routing makes it necessary
The readme's useful patterns section is about a problem that has nothing to do with data fetching, and it is the most opinionated part of the documentation.
Next.js has a very opinionated file-based routing mechanism that does not allow a file in the pages folder without it being considered a page. The readme says plainly that this does not allow for much organisation, and then describes what the library makes possible: treat the pages folder as a folder of entry points and organise the files elsewhere.
The tree it gives is the whole idea in one picture, with a pages directory holding the routes and a routes directory holding the components and helpers for each of them, so each route folder becomes its own small application with its own components and its own helpers.
The reason the library enables this is structural rather than cosmetic. Because a data hook is declared on a component and collected through the dataHooks property, a component in a routes directory can be imported by a page in the pages directory without the component itself becoming a page, and the page still works out which data to fetch from the static property of the component it renders.
The repository has a working example of this layout in the examples directory, in a project called next-data-hooks-example, and the readme points at it for ideas on organising static data calls.
A 2021 release line with a maintained tree
The release history and the development configuration tell slightly different stories, and the second is the more recent.
The published versions are from 2021, 0.4.0 on 2021-01-02, 0.5.0 two days later on 2021-01-04, and 0.5.1 on 2021-11-12. The manifest sits at 0.5.2, and the last push to the repository was on 2026-10-18, so the tree is being touched even though nothing has been published in a long time.
The development dependencies place the project in its era quite precisely. Next is pinned at 13.4.19 and create-next-app at the same version, Jest is at 28.1.3, the ESLint is at 8.57.1 with the React and accessibility plugins, and the React types are at 19.2.14. The bundler is Rollup with the Babel and node-resolve plugins, and the build script is a Node script of the project's own rather than a command from a framework, which is how a Babel plugin project keeps its plugin out of the main bundle.
The repository layout has the pieces you would expect from a small library with a Babel plugin. There is a .babelrc at the root, a src directory, a rollup configuration, a Jest configuration, an e2e directory, the scripts directory with the build, an examples directory with the worked example, and a renovate configuration, which is how the pinned dependency versions get their updates.
So the practical advice for a reader is to check the compatibility first. The library is built against the Next.js 13 generation and the Babel preset of that era, and a project on a newer Next.js with a different bundler setup has to confirm that the plugin still applies before it relies on the code elimination step.
Editorial conclusion
next-data-hooks fits a Next.js codebase whose data fetching has grown into one large query per page, because the library lets each component own its own query while the server side props are still fetched once, on the server, as Next.js requires. It is the wrong choice if you want more than organisation, since the readme is explicit that it offers no capabilities beyond vanilla Next.js, and it is the wrong choice if you are on a build setup without Babel, because the plugin that removes the server side code from the client bundle is part of the installation. Before you adopt it, read the linked issue about why the data hooks are not parameterised, and plan the migration around the _app provider, because without the provider the hooks have nothing to read from.
Frequently asked questions
What does next-data-hooks do?
It is a small library that lets you write Next.js data queries as React hooks, by lifting the results of getStaticProps and getServerSideProps into React Context. A query is declared next to the component that needs it, collected through a dataHooks static property, and executed on the server once per page.
Does next-data-hooks add capabilities to Next.js?
No. The readme is explicit that the primary thing the library offers is a pattern for organising getStaticProps and getServerSideProps, and that it offers no more capabilities than vanilla Next.js. It links to an open issue asking why the data hooks are not parameterised.
What is the Babel plugin for?
The next-data-hooks/babel plugin enables code elimination so that server-side code inside a data hook does not reach the client bundle. It has to be added to the .babelrc file alongside the next/babel preset, and the readme warns not to forget that step.
How do I get types for the context in a data hook?
Importing a data hook also imports its return type, and the library ships the isServerSidePropsContext type guard so a hook used only in getServerSideProps can narrow the context type and check at runtime that it is in the right place.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ricokahler-next-data-hooks)Was this useful?