{"id":15011786,"url":"https://github.com/lukasbombach/next-super-performance","last_synced_at":"2025-04-07T07:12:35.007Z","repository":{"id":40631114,"uuid":"184028193","full_name":"LukasBombach/next-super-performance","owner":"LukasBombach","description":"The case of partial hydration (with Next and Preact)","archived":false,"fork":false,"pushed_at":"2020-10-16T19:20:24.000Z","size":1002,"stargazers_count":416,"open_issues_count":10,"forks_count":17,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-04T12:54:23.965Z","etag":null,"topics":["hydration","javascript","nextjs","partial-hydration","performance","preact","reactjs","webdevelopment"],"latest_commit_sha":null,"homepage":"https://medium.com/spring-media-techblog/how-we-achieved-the-best-web-performance-with-partial-hydration-20fab9c808d5","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LukasBombach.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-29T08:10:34.000Z","updated_at":"2025-01-13T21:35:15.000Z","dependencies_parsed_at":"2022-08-01T00:08:17.310Z","dependency_job_id":null,"html_url":"https://github.com/LukasBombach/next-super-performance","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasBombach%2Fnext-super-performance","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasBombach%2Fnext-super-performance/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasBombach%2Fnext-super-performance/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LukasBombach%2Fnext-super-performance/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LukasBombach","download_url":"https://codeload.github.com/LukasBombach/next-super-performance/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247608153,"owners_count":20965952,"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":["hydration","javascript","nextjs","partial-hydration","performance","preact","reactjs","webdevelopment"],"created_at":"2024-09-24T19:41:42.059Z","updated_at":"2025-04-07T07:12:34.977Z","avatar_url":"https://github.com/LukasBombach.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🏎 next-super-performance\n\n\u003cimg src=\"./docs/images/dyncamic-elements-in-static-page.png\" align=\"right\"\n     title=\"Dynamic elements in a static page\" width=\"430\"\u003e\n\nPartial hydration for [Next.js](https://github.com/zeit/next.js/) with [Preact X](https://github.com/developit/preact).\n\n---\n\n**Explanation:** At spring we are creating websites for newspapers and we are very, **very** performance aware.\n\nNewspapers are mostly static pages. Now if we were to create a single page application we would\ncreate a huge bundle with mostly unnecessary code.\n\nThis does not only mean that users wait for a big file to download, but [as Addy Osmami points out](https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4)\nthere is a huge cost in performance with parsing and executing code. As a vague rule of thumb we can\nsay, the bigger your bundle, the worse your performance.\n\n---\n\n\u003cbr/\u003e\n\nThat is why we aim to cut bundle size by only shipping the code we actually need in the client and leave the rest to server side rendering.\n\n## Overview\n\nThis repo ist still a proof of concept, we will continue to work on this and implement our work\nas 2 packages:\n\n- `pool-attendant-preact` A library that implements partialy hydration with preact x\n- `next-super-performance` A Next.js plugin that uses pool-attendant-preact to improve client side performance\n\nOn top of partial hydration we will implement loading strategies including `critical CSS`, `critical JS`, `lazy loading`, `preloading ressources`, etc. as part of next-super-performance.\n\n## Documentation\n\nFor now we have a partial hydration POC for Next.js and this is how it works. When you create a `next.config.js` and use our plugin like so\n\n```js\nconst withSuperPerformance = require(\"next-super-performance\");\nmodule.exports = withSuperPerformance();\n```\n\n2 things will happen:\n\n- `React` will be replaced by `Preact` because it is only 3KB\n- Next.js' main client JavaScript file will be discarded and replaced by a JavaScript file in your control\n\nThat means you have to create a `client.js` in your app's root folder that will act as the entry\npoint for the JavaScript that will be sent to the client. We do this to give you full control of\nwhat you want your users to download and, very importantly, to choose the loading strategy that is\nright for you.\n\nNow `pool-attendant-preact` comes into play. pool-attendant-preact exports 3 APIs for you:\n\n- `withHydration` a HOC that lets you mark your components for hydration\n- `hydrate` a function to hydrate marked components in the client\n- `HydrationData` a component that writes serialized props to the client, like `NEXT_DATA`\n\n\u003cimg src=\"./docs/images/dyncamic-teaers.png\" align=\"left\"\n     title=\"Dynamic elements in a static page\" width=\"430\"\u003e\n\nLet's explain this by example. Say you have a Next app with a `header`, a `main` section and `teaser`s (which may just be images with a text and a headline, for instance). For the sake of this example, let's\ntry and make the teasers 2 \u0026 3 dynamic (just to pick some items on the page) and leave the rest static.\n\nHere is how you would do it:\n\nInstall next-super-performance\n\n```console\nnpm i next-super-performance --save\n```\n\nCreate a `next.config.js` and use the plugin\n\n```js\nconst withSuperPerformance = require(\"next-super-performance\");\nmodule.exports = withSuperPerformance();\n```\n\nModify your `package.json` to make Next use Preact properly ([this will alias `react` to `preact` and then start the original next scripts without modification](./packages/next-super-performance/bin/index.js)):\n\n```js\n  \"scripts\": {\n    \"dev\": \"next:performance dev\",\n    \"start\": \"next:performance start\",\n    \"build\": \"next:performance build\"\n  },\n```\n\nCreate `pages/index.js`\n\n```jsx\nimport Header from \"../components/header\";\nimport Main from \"../components/main\";\nimport { HydrationData } from \"next-super-performance\";\n\nexport default function Home() {\n  return (\n    \u003csection\u003e\n      \u003cHeader /\u003e\n      \u003cMain /\u003e\n      \u003cHydrationData /\u003e\n    \u003c/section\u003e\n  );\n}\n```\n\nThe important part here is `\u003cHydrationData /\u003e` which will insert something like this:\n\n```html\n\u003cscript type=\"application/hydration-data\"\u003e\n  {\"1\":{\"name\":\"Teaser\",\"props\":{\"column\":2}},\"2\":{\"name\":\"Teaser\",\"props\":{\"column\":3}}}\n\u003c/script\u003e\n```\n\nThese are the names and props of the components that will be hydrated.\n\nTo tell your app that a particular component should be hydrated use `withHydration`. Our `main.js`\ncould look like this:\n\n```jsx\nimport Teaser from \"./teaser\";\nimport { withHydration } from \"next-super-performance\";\n\nconst HydratedTeaser = withHydration(Teaser);\n\nexport default function Body() {\n  return (\n    \u003cmain\u003e\n      \u003cTeaser column={1} /\u003e\n      \u003cHydratedTeaser column={2} /\u003e\n      \u003cHydratedTeaser column={3} /\u003e\n\n      \u003cTeaser column={1} /\u003e\n      \u003cTeaser column={2} /\u003e\n      \u003cTeaser column={3} /\u003e\n\n      \u003cTeaser column={1} /\u003e\n      \u003cTeaser column={2} /\u003e\n      \u003cTeaser column={3} /\u003e\n    \u003c/main\u003e\n  );\n}\n```\n\nIn line 4 we have created a component that will be hydrated in the client and we use it 2 times\non our page with different props.\n\n`withHydration` will prepend your component with a _marker_ so that it can be rendered on the\nserver and be found in your HTML on the client. So `\u003cHydratedTeaser column={2} /\u003e` will become\n\n```jsx\n\u003cFragment\u003e\n  \u003cscript type=\"application/hydration-marker\" /\u003e\n  \u003cTeaser column={2} /\u003e\n\u003c/Fragment\u003e\n```\n\nThe last and most crucial part is your `client.js` which is the code that will ship to your users\nand which is where you will hydrate your components. For a single component (`Teaser`) it can be\nsimple as that.\n\n```jsx\nimport { hydrate } from \"next-super-performance\";\nimport Teaser from \"./components/teaser\";\n\nhydrate([Teaser]);\n```\n\nOh, `next-super-performance` comes with `pool-attendant-preact` which is why you import everything\nfrom here instead of from `pool-attendant-preact`. It just imports and exports `withHydration`,\n`hydrate` and `HydrationData` for convenience.\n\n`hydrate` will find the components you have marked using `withHydration` and use the data from\n`\u003cHydrationData /\u003e` to hydrate them with the components you have passed to them as an array.\n\nThis will require you to import the components you want to use in the client (and pass them\nto the `hydrate` function). Because `client.js` is the entry point for all you client code, this\nalso means you will see and control exactly which code you send to your users. Apart from Preact\nnothing else will be shipped.\n\nIf your components have dependencies on their own, those dependencies will be \"natuarally\" shipped\nas well because `client.js` is your entry and every dependendcy will be resolved through webpack.\n\n## Status\n\nThis repo is a POC and something we will build on. To try this out, clone this repository and then run\n\n```sh\n# Init Preact Git Sumodule\ngit submodule init\ngit submodule update\n\n# Install dependencies\nyarn\n\n# Build Preact\ncd packages/preact\nyarn build\n\n# Build the pool-attendant-preact package\n# └─ cd to the pool-attendant-preact dir\ncd ...\ncd packages/pool-attendant-preact\n# └─ build the package\nyarn build\n\n# cd to the app dir\ncd ...\ncd packages/app\n\n# run the app\nyarn dev\n```\n\n## Conclusion\n\nThis POC seems to work quite well, we could drastically reduce our bundle size. There is still\na lot to do though. Next.js still bundles code we don't want to see in the client (like `core-js`).\nAlso we aim to implement tools and APIs to create a language for performance-critical aspects\nof your code to provide you with tools to define your critical rendering path.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasbombach%2Fnext-super-performance","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukasbombach%2Fnext-super-performance","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasbombach%2Fnext-super-performance/lists"}