{"id":15640519,"url":"https://github.com/amannn/next-client-script","last_synced_at":"2025-09-01T03:38:54.969Z","repository":{"id":37856153,"uuid":"276629559","full_name":"amannn/next-client-script","owner":"amannn","description":"Supercharge the performance of your Next.js apps by using a minimal client runtime 🚀","archived":false,"fork":false,"pushed_at":"2024-08-28T07:00:05.000Z","size":448,"stargazers_count":77,"open_issues_count":3,"forks_count":8,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-24T17:08:52.097Z","etag":null,"topics":["nextjs","performance","react"],"latest_commit_sha":null,"homepage":"https://next-client-script.vercel.app","language":"TypeScript","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/amannn.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-02T11:33:43.000Z","updated_at":"2025-03-04T13:55:40.000Z","dependencies_parsed_at":"2024-10-23T05:25:45.995Z","dependency_job_id":null,"html_url":"https://github.com/amannn/next-client-script","commit_stats":{"total_commits":23,"total_committers":1,"mean_commits":23.0,"dds":0.0,"last_synced_commit":"e1f33f719bb12dc1ca10170169a470ad015d5f33"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/amannn/next-client-script","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amannn%2Fnext-client-script","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amannn%2Fnext-client-script/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amannn%2Fnext-client-script/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amannn%2Fnext-client-script/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amannn","download_url":"https://codeload.github.com/amannn/next-client-script/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amannn%2Fnext-client-script/sbom","scorecard":{"id":188305,"data":{"date":"2025-08-11","repo":{"name":"github.com/amannn/next-client-script","commit":"1e30eeeced9bdd874eeb2f77d66e990026cf23af"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.5,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/24 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/main.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/amannn/next-client-script/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/amannn/next-client-script/main.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/main.yml:11: update your workflow using https://app.stepsecurity.io/secureworkflow/amannn/next-client-script/main.yml/master?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 8 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"70 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-257v-vj4p-3w2h","Warn: Project is vulnerable to: GHSA-7gc6-qh9x-w6h8","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-7r28-3m3f-r2pr","Warn: Project is vulnerable to: GHSA-r8j5-h5cx-65gg","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-vxf5-wxwp-m7g9","Warn: Project is vulnerable to: GHSA-25mp-g6fv-mqxx","Warn: Project is vulnerable to: GHSA-c59h-r6p8-q9wc","Warn: Project is vulnerable to: GHSA-qpjv-v59x-3qc4","Warn: Project is vulnerable to: GHSA-w7rc-rwvf-8q5r","Warn: Project is vulnerable to: GHSA-r683-j2x4-v87g","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-cwx2-736x-mf6w","Warn: Project is vulnerable to: GHSA-v39p-96qg-c8rf","Warn: Project is vulnerable to: GHSA-8v63-cqqc-6r2c","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-566m-qj78-rww5","Warn: Project is vulnerable to: GHSA-hwj9-h5mp-3pm3","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-vx3p-948g-6vhq","Warn: Project is vulnerable to: GHSA-4wf5-vphf-c2xc","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-9gr3-7897-pp7m","Warn: Project is vulnerable to: GHSA-fmvm-x8mv-47mj","Warn: Project is vulnerable to: GHSA-g77x-44xx-532m","Warn: Project is vulnerable to: GHSA-7gfc-8cq8-jh5f","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-gp95-ppv5-3jc5","Warn: Project is vulnerable to: GHSA-54xq-cgqr-rpm3","Warn: Project is vulnerable to: GHSA-wpg7-2c88-r8xv","Warn: Project is vulnerable to: GHSA-pq67-2wwv-3xjx","Warn: Project is vulnerable to: GHSA-8cj5-5rvv-wf4v","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T20:10:58.085Z","repository_id":37856153,"created_at":"2025-08-16T20:10:58.085Z","updated_at":"2025-08-16T20:10:58.085Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273070097,"owners_count":25040213,"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","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["nextjs","performance","react"],"created_at":"2024-10-03T11:36:42.446Z","updated_at":"2025-09-01T03:38:54.942Z","avatar_url":"https://github.com/amannn.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [!WARNING]  \n\u003e This project is in maintainence mode now, check out [React Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components#server-components) instead!\n\n# next-client-script\n\n\u003e Supercharge the performance of your Next.js apps by using a minimal client runtime that avoids full-blown hydration. 🚀\n\n## The problem\n\nBy default, Next.js adds the code to your client bundle that is necessary to execute your whole page. At a minimum this includes React itself, the components to render the markup and if relevant, the data that is necessary to rehydrate the markup (result from `getInitialProps` and friends).\n\nFor content heavy sites this [can cause performance issues](https://developers.google.com/web/updates/2019/02/rendering-on-the-web#rehydration) since the page is unresponsive while the client bundle is being executed.\n\nRecently, an [early version of removing the client side bundle](https://github.com/vercel/next.js/pull/11949) was shipped to Next.js which doesn't suffer from performance problems caused by hydration. However, for a typical website you'll likely still need some JavaScript on the client side to deliver a reasonable user experience.\n\n## This solution\n\nThis is a Next.js plugin that is intended to be used in conjunction with disabled runtime JavaScript. You can add client bundles on a per-page basis that only sprinkle a tiny bit of JavaScript over otherwise completely static pages.\n\nThis allows for the same [architecture that Netflix has chosen for their public pages](https://medium.com/dev-channel/a-netflix-web-performance-case-study-c0bcde26a9d9).\n\n**Benefits:**\n\n- Keep the React component model for rendering your markup server side\n- Use the Next.js development experience and build pipeline for optimizing the server response\n- A client side runtime for components is opt-in\n- Serializing data for the client is opt-in\n\nThe tradeoff is that you can't use any client-side features of React (state, effects, event handlers, …). Note that some features of Next.js might not be available (yet) – e.g. code splitting via `dynamic` within a page.\n\n→ [Demo deployment](https://next-client-script.vercel.app/) ([source](https://github.com/amannn/next-client-script/tree/master/packages/example))\n\n\n## Compatibility\n\n⚠️ **Important:** To achieve the desired effect, this plugin modifies the webpack configuration that Next.js consumes. Similar as with other Next.js plugins, it's possible that this plugin will break when there are updates to Next.js. I'm keeping the plugin updated so that it continues to work with new versions of Next.js.\n\n| Next.js version  | Plugin version |\n| ------------- | ------------- |\n| ^10.0.0, ^9.5.0 | 0.1.0 |\n| ~9.4.0 | 0.0.6 |\n\nLatest version tested: 10.0.5\n\n## Getting started\n\n### Minimum setup\n\n1. Add a client script for a page.\n\n```js\n// ./src/client/index.ts\nconsole.log('Hello from client.');\n```\n\n2. Add this plugin to your `next.config.js` and reference your client script.\n\n```js\nconst withClientScripts = require('next-client-script/withClientScripts');\n\n// Define which paths will cause which scripts to load\nmodule.exports = withClientScripts({\n  '/': './src/client/index.ts',\n  // You can use parameters as provided by path-to-regexp to match routes dynamically.\n  '/products/:id': './src/client/product.ts'\n})();\n```\n\n3. Add a [custom document to your app](https://nextjs.org/docs/advanced-features/custom-document) and add the `\u003cClientScript /\u003e` component as the last child in the body.\n\n```diff\n+ import ClientScript from 'next-client-script/ClientScript';\n\n  // ...\n\n+   \u003cClientScript /\u003e\n  \u003c/body\u003e\n```\n\n4. **Recommended**: Disable the runtime JavaScript for the pages with separate client scripts:\n\n```js\n// ./pages/index.ts\nexport const config = {\n  unstable_runtimeJS: false\n};\n```\n\nNote that you can mix this approach with the traditional hydration approach, to optimize the performance of critical pages while keeping the flexibility of using React on the client side for other pages.\n\nSee [the example folder](https://github.com/amannn/next-client-script/blob/master/packages/example) for a project demonstrating this setup.\n\n### Widget usage\n\nTo help with a component-oriented approach for client-side code, this library contains convenience APIs that help with passing data to the client and initializing widgets.\n\nUse the `ClientWidget` component to mark an entry point for the client side and to optionally pass in some data.\n\n```js\n// Counter.js\nimport ClientWidget from 'next-client-script/ClientWidget';\nimport styles from './Counter.module.scss';\n\nexport default function Counter({initialCount = 2}) {\n  return (\n    \u003cClientWidget className={styles.root} data={{initialCount}}\u003e\n      \u003cp className={styles.label}\u003e\n        Count: \u003cspan className={styles.count}\u003e{initialCount}\u003c/span\u003e\n      \u003c/p\u003e\n      \u003cbutton className={styles.button}\u003eIncrement\u003c/button\u003e\n    \u003c/ClientWidget\u003e\n  );\n}\n```\n\nNow you can add a client part for this component that receives the data and adds interactivity.\n\n```js\n// Counter.client.js\nimport styles from './Counter.module.scss';\n\nexport default function initCounter(rootNode, data) {\n  let count = data.initialCount;\n\n  const countNode = rootNode.querySelector(`.${styles.count}`);\n  const buttonNode = rootNode.querySelector(`.${styles.button}`);\n\n  buttonNode.addEventListener('click', () =\u003e {\n    count++;\n    countNode.textContent = count;\n  });\n}\n\n// This will be passed to `querySelectorAll` to find all widgets on the page\ninitCounter.selector = `.${styles.root}`;\n```\n\nAs a last step, you need to reference the client counter code in your client script:\n\n```js\nimport initWidgets from 'next-client-script/initWidgets';\nimport Counter from 'components/Counter/Counter.client';\n\ninitWidgets([Counter]);\n```\n\n## Prior art \u0026 credits\n\n- [A Netflix Web Performance Case Study](https://medium.com/dev-channel/a-netflix-web-performance-case-study-c0bcde26a9d9) by [Addy Osmani](https://twitter.com/addyosmani)\n- [next-critical](https://github.com/stroeer/next-critical) by [Lukas Bombach](https://github.com/stroeer/next-critical)\n\nI really hope that React will solve hydration problems in the future with [partial hydration](https://github.com/facebook/react/pull/14717) and [server-side components](https://github.com/facebook/react/tree/master/fixtures/blocks), but I think a tiny bit of vanilla JavaScript on the client side is really hard to beat.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famannn%2Fnext-client-script","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famannn%2Fnext-client-script","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famannn%2Fnext-client-script/lists"}