{"id":12127,"date":"2025-12-27T22:47:05","date_gmt":"2025-12-27T22:47:05","guid":{"rendered":"https:\/\/putridparrot.com\/blog\/?p=12127"},"modified":"2025-12-27T22:49:26","modified_gmt":"2025-12-27T22:49:26","slug":"auto-discovery-using-vite-and-react","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/auto-discovery-using-vite-and-react\/","title":{"rendered":"Auto-discovery using Vite (and React)"},"content":{"rendered":"<p>I&#8217;m messing around with a Shell application in React and wanting to load information from apps\/components that are added to the shell dynamically, i.e. via auto-discovery.<\/p>\n<p><em>Now, we&#8217;re using React for this example so we can create React apps using Vite for what we will call, our components, as these can be loaded into React as components. The reason to create these as standalone apps would be for use to develop against.<\/em><\/p>\n<p>So let&#8217;s assume I have a main application, the Shell app and this can get routes for functionality (our apps\/components) which may get added later on in the development process, basically a pluggable type of architecture which just uses React components along with those components supplying routing information to allow us to plug them into the Shell.<\/p>\n<p>To give it more context, I build a Shell application with Search and later on want to add a Document UI, so it&#8217;d be nice if the Document can be worked on separately and when ready, deployed to a specific locations where the Shell (upon refresh) can discover the new component and wire into the Shell.<\/p>\n<p>Vite has a feature <em>import.meta.glob<\/em> which we can use to discover our <em>routes.tsx<\/em> that are deployed to a specific path, i.e.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nconst modules = import.meta.glob&lt;Record&lt;string, unknown&gt;&gt;(\r\n  &#039;..\/..\/apps\/*\/src\/routes.tsx&#039;, { eager: true }\r\n);\r\n<\/pre>\n<p>This will return keys for the file paths and values being the functions that import from the files or modules. For example it might locate components such as<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\napps\/search\/src\/routes.tsx\r\napps\/document\/src\/routes.tsx\r\napps\/export\/src\/routes.tsx\r\napps\/settings\/src\/routes.tsx\r\n<\/pre>\n<p>If <em>eager<\/em> is missing (or false) then Vite lazy imports the functions and you&#8217;d then need to call them yourself, with <em>eager:true<\/em>, Vite imports the modules immediately.<\/p>\n<p>If we use something like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n\r\nimport type { RouteObject } from &#039;react-router-dom&#039;;\r\n\r\nconst modules = import.meta.glob&lt;Record&lt;string, unknown&gt;&gt;(\r\n  &#039;..\/..\/apps\/*\/src\/routes.tsx&#039;, { eager: true }\r\n);\r\n\r\nexport const allAppRoutes: RouteObject&#x5B;] = Object.values(modules)\r\n  .flatMap((mod) =&gt; {\r\n    const arr = Object.values(mod).find(\r\n      (v): v is RouteObject&#x5B;] =&gt; Array.isArray(v) &amp;&amp; \r\n        v.every(item =&gt; typeof item === &#039;object&#039; &amp;&amp; \r\n           item !== null &amp;&amp; &#039;path&#039; in item &amp;&amp; &#039;element&#039; in item)\r\n    );\r\n    return arr || &#x5B;];\r\n  });\r\n<\/pre>\n<p>Then we could use the routes via the react-router-dom in App.tsx, like this<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nconst baseRoutes = &#x5B;\r\n  { path: &#039;\/&#039;, element: &lt;div&gt;Welcome to the Shell&lt;\/div&gt; },\r\n];\r\n\r\nconst routes = &#x5B;\r\n  ...baseRoutes,\r\n  ...allAppRoutes,\r\n];\r\nreturn (\r\n  &lt;AuthContext.Provider value={auth}&gt;\r\n    &lt;ShellLayout&gt;\r\n      &lt;Suspense fallback={&lt;div&gt;Loading\u2026&lt;\/div&gt;}&gt;\r\n        &lt;Routes&gt;\r\n          {routes.map(({ path, element }) =&gt; (\r\n            &lt;Route key={path} path={path} element={element} \/&gt;\r\n          ))}\r\n        &lt;\/Routes&gt;\r\n      &lt;\/Suspense&gt;\r\n    &lt;\/ShellLayout&gt;\r\n  &lt;\/AuthContext.Provider&gt;\r\n);\r\n<\/pre>\n<p>and finally here&#8217;s an example routes.tsx file for our components<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nimport SearchApp from &#039;.\/App&#039;;\r\n\r\nexport const searchRoutes = &#x5B;\r\n  { path: &#039;\/search\/*&#039;, element: &lt;SearchApp \/&gt; },\r\n];\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m messing around with a Shell application in React and wanting to load information from apps\/components that are added to the shell dynamically, i.e. via auto-discovery. Now, we&#8217;re using React for this example so we can create React apps using Vite for what we will call, our components, as these can be loaded into React [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[243,735],"tags":[],"class_list":["post-12127","post","type-post","status-publish","format-standard","hentry","category-react","category-vite"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/12127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=12127"}],"version-history":[{"count":5,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/12127\/revisions"}],"predecessor-version":[{"id":12135,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/12127\/revisions\/12135"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=12127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=12127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=12127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}