{"id":28350678,"url":"https://github.com/exah/react-universal-data","last_synced_at":"2025-07-12T15:33:21.173Z","repository":{"id":57346984,"uuid":"138864583","full_name":"exah/react-universal-data","owner":"exah","description":"Easy to use React hook for getting data on client and server side with effortless hydration of state","archived":false,"fork":false,"pushed_at":"2020-05-10T14:31:27.000Z","size":469,"stargazers_count":13,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-04T04:09:51.317Z","etag":null,"topics":["async","hook","isomorphic","prefetch","react","react-dom","server-rendering","small-size","ssr","tiny","universal"],"latest_commit_sha":null,"homepage":"https://bundlephobia.com/result?p=react-universal-data","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/exah.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-06-27T10:08:15.000Z","updated_at":"2024-06-09T21:29:13.000Z","dependencies_parsed_at":"2022-09-17T22:50:29.178Z","dependency_job_id":null,"html_url":"https://github.com/exah/react-universal-data","commit_stats":null,"previous_names":["exah/react-get-app-data"],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/exah/react-universal-data","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exah%2Freact-universal-data","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exah%2Freact-universal-data/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exah%2Freact-universal-data/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exah%2Freact-universal-data/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exah","download_url":"https://codeload.github.com/exah/react-universal-data/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exah%2Freact-universal-data/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261029758,"owners_count":23099811,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","hook","isomorphic","prefetch","react","react-dom","server-rendering","small-size","ssr","tiny","universal"],"created_at":"2025-05-27T21:13:07.602Z","updated_at":"2025-07-12T15:33:21.161Z","avatar_url":"https://github.com/exah.png","language":"JavaScript","readme":"# 🗂 react-universal-data\n\n[![](https://flat.badgen.net/npm/v/react-universal-data?cache=600)](https://www.npmjs.com/package/react-universal-data) [![](https://flat.badgen.net/bundlephobia/minzip/react-universal-data?cache=600)](https://bundlephobia.com/result?p=react-universal-data) ![](https://flat.badgen.net/travis/exah/react-universal-data?cache=600) ![](https://flat.badgen.net/coveralls/c/github/exah/react-universal-data)\n\n#### Easy to use hook for getting data on client and server side with effortless hydration of state\n\n- [x] Only 600B minified and gziped\n- [x] Simple hooks API\n- [x] TypeScript\n- [x] Can handle updates\n- [x] Simple cache\n- [x] [Suspense](http://reactjs.org/docs/concurrent-mode-suspense.html) on server side via [`react-ssr-prepass`](https://github.com/FormidableLabs/react-ssr-prepass) 💕\n\n\u003e _This is a NO BULLSHIT hook: just PLUG IT in your components, get ALL THE DATA you need (and some more) both CLIENT- and SERVER-side, HYDRATE that ~~bastard~~ app while SSRing like it's NO BIG DEAL, effortlessly PASS IT to the client and render THE SHIT out of it_\n\u003e\n\u003e [@razdvapoka](https://github.com/razdvapoka)\n\n## 📦 Install\n\n```sh\n$ npm i -S react-universal-data\n```\n\n```sh\n$ yarn add react-universal-data\n```\n\n## 📖 Docs\n\n### `useFetchData`\n\nRequests data and preserves the result to the state.\n\n```ts\ntype useFetchData\u003cT\u003e = (\n  // async function that can return any type of data\n  fetcher: (key: string, context: { isServer: boolean }) =\u003e Promise\u003cT\u003e,\n  // unique key that will be used for storing \u0026 hydrating data while SSR\n  key: string,\n  // use cached value for specified duration of time, by default it will be requested each time\n  ttl?: number\n) =\u003e AsyncState\u003cT\u003e\n```\n\n\u003e ⚠️ The `key` must be unique for the whole application.\n\nReturned object can be in 4 different forms – depending on the promise's state.\n\n```ts\nexport type AsyncState\u003cT\u003e =\n  // initial\n  | { isReady: false; isLoading: false; error: null; result: undefined }\n  // fulfilled\n  | { isReady: true; isLoading: false; error: null; result: T }\n  // pending\n  | { isReady: boolean; isLoading: true; error: Error | null; result?: T }\n  // rejected\n  | { isReady: false; isLoading: false; error: Error; result?: T }\n```\n\n\u003cdetails\u003e\u003csummary\u003e👀 Fetch a sample post via \u003ca href=\"https://jsonplaceholder.typicode.com\"\u003ejsonplaceholder.typicode.com\u003c/a\u003e API\u003c/summary\u003e\n\n```js\nimport React from 'react'\nimport { useFetchData } from 'react-universal-data'\n\nconst fetchPost = (id) =\u003e\n  fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)\n    .then((response) =\u003e response.json())\n\nfunction Post({ id }) {\n  const { isReady, isLoading, result, error } = useFetchData(fetchPost, id)\n\n  if (isLoading) {\n    return \u003cp\u003eLoading...\u003c/p\u003e\n  }\n\n  if (error) {\n    return \u003cp\u003eOh no: {error.message}\u003c/p\u003e\n  }\n\n  // You can depend on `isReady` flag to ensure data loaded correctly\n  if (isReady) {\n    return (\n      \u003carticle\u003e\n        \u003ch2\u003e{result.title}\u003c/h2\u003e\n        \u003cp\u003e{result.body}\u003c/p\u003e\n      \u003c/article\u003e\n    )\n  }\n\n  return null\n}\n```\n\u003c/details\u003e\n\nAs the hook depends on the `fetcher` function identity to be stable, please, wrap it inside `useCallback` or define it outside of the render function to prevent infinite updates.\n\n```js\nimport React, { useCallback } from 'react'\nimport { useFetchData } from 'react-universal-data'\n\nfunction UserPosts({ userId }) {\n  const fetchPosts = useCallback(() =\u003e (\n    fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)\n      .then((response) =\u003e response.json())\n  ), [userId]) // will pereform update if value changed\n\n  const { result = [] } = useFetchData(fetchPosts, 'user-posts')\n\n  return (\n    \u003cul\u003e\n      {result.map((post) =\u003e \u003cli key={post.id}\u003e{post.title}\u003c/li\u003e)}\n    \u003c/ul\u003e\n  )\n}\n```\n\n\u003cdetails\u003e\u003csummary\u003e👀 Create a custom hook for it\u003c/summary\u003e\n\n```js\nimport React, { useCallback } from 'react'\nimport { useFetchData } from 'react-universal-data'\n\nfunction useFetchUserPosts(userId) {\n  return useFetchData(\n    useCallback(() =\u003e (\n      fetch(`https://jsonplaceholder.typicode.com/posts?userId=${userId}`)\n        .then((response) =\u003e response.json())\n    ), [userId]),\n    'user-posts'\n  )\n}\n\nfunction UserPosts({ userId }) {\n  const { result = [] } = useFetchUserPosts(userId)\n\n  return (\n    \u003cul\u003e\n      {result.map((post) =\u003e \u003cli key={post.id}\u003e{post.title}\u003c/li\u003e)}\n    \u003c/ul\u003e\n  )\n}\n```\n\u003c/details\u003e\n\n### `getInitialData`\n\nHandles `useFetchData` on server side and gathers results for [hydration](#hydrateInitialData) in the browser.\n\n```ts\ntype getInitialData = (element: JSX.Element) =\u003e Promise\u003c[string, any][]\u003e\n```\n\n```js\n// server.js\nimport React from 'react'\nimport { renderToString } from 'react-dom/server'\nimport { getInitialData } from 'react-universal-data'\nimport { App } from './App'\n\nasync function server(req, res) {\n  const element = \u003cApp /\u003e\n\n  const data = await getInitialData(element).catch((error) =\u003e /* handle error */)\n  const html = renderToString(\n    \u003chtml\u003e\n      \u003cbody\u003e\n        \u003cdiv id='app'\u003e{element}\u003c/div\u003e\n        \u003cscript\n          dangerouslySetInnerHTML={{\n            __html: `window._ssr = ${JSON.stringify(data)};`,\n          }}\n        /\u003e\n        \u003cscript src='/client.js' /\u003e\n      \u003c/body\u003e\n    \u003c/html\u003e\n  )\n\n  res.write('\u003c!DOCTYPE html\u003e')\n  res.write(html)\n  res.end()\n}\n```\n\n### `hydrateInitialData`\n\nHydrates initial data gathered with [`getInitialData`](#getInitialData) before rendering the app in the browser.\n\n```ts\ntype hydrateInitialData = (initial: [string, any][]) =\u003e void\n```\n\n```js\n// client.js\nimport React from 'react'\nimport ReactDOM from 'react-dom'\nimport { hydrateInitialData } from 'react-universal-data'\nimport { App } from './App'\n\nhydrateInitialData(window._ssr || [])\nReactDOM.hydrate(\u003cApp /\u003e, document.getElementById('app'))\n```\n\n\n## 💻 Demo\n\n[![Edit react-universal-data-ssr](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/react-universal-data-ssr-jp9el?fontsize=14\u0026hidenavigation=1\u0026module=%2Fsrc%2FApp.js\u0026theme=dark)\n\n## 🔗 Related\n\n### Packages\n\n- [`react-ssr-prepass`](https://github.com/FormidableLabs/react-ssr-prepass) - server-side dependency\n- [`ya-fetch`](https://github.com/exah/ya-fetch) - a lightweight wrapper around `fetch`\n\n### Real world usages\n\n- [kayway.me](https://github.com/exah/kayway)\n- [goremykina.com](https://github.com/exah/goremykina)\n- [strelkamag.com](https://strelkamag.com)\n\n---\n\nMIT © [John Grishin](http://johngrish.in)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexah%2Freact-universal-data","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexah%2Freact-universal-data","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexah%2Freact-universal-data/lists"}