{"id":28835719,"url":"https://github.com/odonno/monorepo-migration","last_synced_at":"2025-10-20T08:40:23.821Z","repository":{"id":75809548,"uuid":"600412319","full_name":"Odonno/monorepo-migration","owner":"Odonno","description":"Example repository on how to migrate a monorepo from Next.js to Astro","archived":false,"fork":false,"pushed_at":"2023-02-22T11:08:11.000Z","size":468,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-19T10:11:31.775Z","etag":null,"topics":["astrojs","migration","monorepo","monorepository","nextjs","react","turborepo"],"latest_commit_sha":null,"homepage":"","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/Odonno.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-02-11T12:15:54.000Z","updated_at":"2024-09-13T01:36:18.000Z","dependencies_parsed_at":"2023-02-27T00:31:33.310Z","dependency_job_id":null,"html_url":"https://github.com/Odonno/monorepo-migration","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Odonno/monorepo-migration","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Odonno%2Fmonorepo-migration","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Odonno%2Fmonorepo-migration/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Odonno%2Fmonorepo-migration/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Odonno%2Fmonorepo-migration/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Odonno","download_url":"https://codeload.github.com/Odonno/monorepo-migration/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Odonno%2Fmonorepo-migration/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280057040,"owners_count":26264834,"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-10-20T02:00:06.978Z","response_time":62,"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":["astrojs","migration","monorepo","monorepository","nextjs","react","turborepo"],"created_at":"2025-06-19T10:10:38.653Z","updated_at":"2025-10-20T08:40:23.812Z","avatar_url":"https://github.com/Odonno.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Case study on migrating a front-end application with maximum code reuse\n\nWith the constant evolution of the JavaScript ecosystem, we see a new way to build a front-end application almost every day, whether it is a new framework or a new pattern/library. A friend of mine asked me this question one day: how do you migrate a live front-end application to take advantage of a new framework without having to rewrite the entire application?\n\n## The starting point\n\nLet's start with a Next.js application in a mono-repository. We want to migrate our Next.js app to Astro. So our repository will contain the following projects:\n\n2 hosts projects:\n\n- `nextjs` a Next.js app\n- `astro` an Astro app\n\n2 apps projects:\n\n- `docs` a documentation web app based on Storybook\n- `home` the web app code used by each host project\n\nA list of packages underneath:\n\n- `data` a list of types and constants that can be used throughout the repository\n- `eslint-config-custom` configurations for eslint\n- `functions` a list of functions that can be used throughout the repository\n- `layouts` a list of the layouts used in the pages\n- `next-ui-wrapper` a set of components specific to Next.js (built on top of the ui library)\n- `tsconfig` the default configuration for TypeScript projects\n- `ui` a list of the components used in the app\n\nThe source code is hosted on GitHub here: https://github.com/Odonno/monorepo-migration\n\nN.B. This folder structure is for demonstration purposes only. Some parts of this architecture can be added or removed in a more concrete application.\n\n## Stateful apps\n\nThis is a simple view of modern web applications, but even with the myriad of frontend frameworks (and now meta-frameworks), we can divide pages into 2 categories: static/stateless pages and stateful pages.\n\nCreating and migrating stateless pages is fairly straightforward. However, with stateful pages, we need to take care of the state and make sure we are using the right abstraction instead of being too attached to a framework. Let's look at what can be considered state:\n\n- Remote states: state from an API call, state persisted in the URL (route or query params)\n- Local states: internal state of a component, in-memory global state within the application (like Redux)\n\nIf the in-memory global state makes you too attached to the framework, then the best counter would be to store that state in local storage. That way, any other framework could interpret the data in local storage and \"restart\" from there.\n\n## Pages migration\n\nTo make the migration of all the pages of our app as painless as possible, we need to have as little code as possible. Hopefully with Next.js and Astro this will be quite easy.\n\n_Reusing a React page component within a Next.js page:_\n\n```ts\nexport { default } from \"home/pages\"; // pages/index.tsx\n```\n\n_Reusing a React page component within an Astro page:_\n\n```astro\n---\nimport Layout from \"../layouts/Layout.astro\";\nimport Home from \"home/pages\";\n---\n\n\u003cLayout\u003e\n  \u003cHome /\u003e\n\u003c/Layout\u003e\n```\n\nIn Astro, this pattern works well for purely static components but if you have dynamic components, you will need to use [template directives](https://docs.astro.build/en/reference/directives-reference/#client-directives).\n\n_Reusing a dynamic React page component within an Astro page:_\n\n```astro\n---\nimport Layout from \"../layouts/Layout.astro\";\nimport Forms from \"home/pages/forms\";\n---\n\n\u003cLayout\u003e\n  \u003cForms client:load /\u003e\n\u003c/Layout\u003e\n```\n\nRemember, this is the easy way. Astro provides a more decoupled component structure. You could remove the `home` project that contains the React pages and use React components directly. Or even better, rewrite React components in Astro. This is perfectly fine, but then you are tied to a framework again.\n\n## API endpoints migration\n\nAnother important feature of meta-framework is the API endpoints. It is also possible to do a soft migration of these, but with a bit more boilerplate code.\n\nLet's start with the Next.js example:\n\n```ts\nexport default function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\u003cGridApiResponse\u003e\n) {\n  switch (req.method) {\n    case \"GET\":\n      convertApiResponse(res, GameApi.get(response));\n    case \"POST\":\n      convertApiResponse(res, GameApi.post(response, req.body));\n    case \"DELETE\":\n      convertApiResponse(res, GameApi.del(response));\n    default:\n      res.setHeader(\"Allow\", [\"GET\", \"POST\", \"DELETE\"]).status(405);\n  }\n}\n```\n\nThe code for each API endpoint is available in a shared package. We still need to map the return value of each API function (`get`, `post`, `del`) using a mapping function called `convertApiResponse`.\n\n```ts\nexport const convertApiResponse = \u003cT\u003e(\n  res: NextApiResponse\u003cT\u003e,\n  apiResponse: ApiResponse\n) =\u003e {\n  res.status(apiResponse.status || 200).json(apiResponse.response);\n};\n```\n\nThis is a special mapping function and it will only work for Next.js. When we work with Astro, we will have to change it a little:\n\n```ts\nexport const convertApiResponse = (apiResponse: ApiResponse) =\u003e {\n  return {\n    status: apiResponse.status || 200,\n    body: JSON.stringify(apiResponse.response),\n  };\n};\n```\n\n## Going beyond\n\nFrom what we have seen, migrating a large codebase has become fairly straightforward. Using a mono-repository tool like [turborepo](https://turbo.build/) for this kind of task can open up some possibilities:\n\n- A powerful tool for building and developing complex applications\n- With the right tool (e.g. feature flipping), we can test activating/deactivating a host project in real time, so that we can easily revert to the previous host modelling if required\n- We can even keep the two hosts alive at the same time and choose which page should be rendered by which host, using a load balancer\n\nHowever, there is still room for improvement, one thing to note is that in order to migrate a large number of pages or API endpoints, we will have to write the same code over and over again… We could imagine a small script that can automatically generate code for pages/api endpoints following the appropriate pattern.\n\nIn this article, we have only described one way to accomplish a migration from a React host to a host that supports React. First of all, this is not an article to prove that Astro is better than Next.js, or vice versa. Then we could have chosen another meta-framework that is built on or supports React, such as Remix.\n\nSpecial thanks to my friend [Jean-Baptiste Vigneron](https://www.jbvigneron.fr).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodonno%2Fmonorepo-migration","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fodonno%2Fmonorepo-migration","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fodonno%2Fmonorepo-migration/lists"}