<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Robert Orliński</title>
    <description>The latest articles on DEV Community by Robert Orliński (@robertorlinski).</description>
    <link>https://dev.to/robertorlinski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1008542%2F7866ba52-6f59-43a0-bda7-01861ee5f3ca.jpeg</url>
      <title>DEV Community: Robert Orliński</title>
      <link>https://dev.to/robertorlinski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/robertorlinski"/>
    <language>en</language>
    <item>
      <title>How to bypass mobile app review thanks to Capacitor, Ionic, and micro frontends 🤯</title>
      <dc:creator>Robert Orliński</dc:creator>
      <pubDate>Mon, 23 Jan 2023 10:31:51 +0000</pubDate>
      <link>https://dev.to/robertorlinski/how-to-bypass-mobile-app-review-thanks-to-capacitor-ionic-and-micro-frontends-a13</link>
      <guid>https://dev.to/robertorlinski/how-to-bypass-mobile-app-review-thanks-to-capacitor-ionic-and-micro-frontends-a13</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclamer: Tutorial was made based on Ionic 6. In case of issues, please refer to Johan's comment: &lt;a href="https://dev.to/maangs/comment/26ji3"&gt;https://dev.to/maangs/comment/26ji3&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Are you tired of sending your app to Apple or Google to review it every time you update it? &lt;/p&gt;

&lt;p&gt;Here I come with the possible solution. Solution because of which you will be able to bypass these reviews 90% of the time or even more.&lt;/p&gt;

&lt;p&gt;As you maybe know, we have 4 ways to build mobile apps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Native - one app for iOS and one for Android,&lt;/li&gt;
&lt;li&gt;Compiled - one codebase compiled to 2 native apps written in &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;React Native&lt;/a&gt; or &lt;a href="https://flutter.dev/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; for instance,&lt;/li&gt;
&lt;li&gt;Hybrid - web app opened in a web view inside of a mobile app - we build web app and then use &lt;a href="https://capacitorjs.com/" rel="noopener noreferrer"&gt;Capacitor&lt;/a&gt; for example to run this web app inside of a native mobile app,&lt;/li&gt;
&lt;li&gt;PWA - basically a web app, hosted on the web but behaving like a mobile app (handling offline mode etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And in this text, I want to show how we can use the third approach to make a mobile app which requires app’s review only once while being uploaded to App Store and Google Play and then it doesn’t need it anymore while being updated.&lt;/p&gt;

&lt;p&gt;When we update such an app, we can just deploy new code to any server, cloud, static-site hosting provider or any other place which fits for web applications, instead of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building the whole app,&lt;/li&gt;
&lt;li&gt;uploading it to App Store and Google Play,&lt;/li&gt;
&lt;li&gt;and then waiting for an approval for the new version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see how it can be done!&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s start with a bit of theory
&lt;/h2&gt;

&lt;p&gt;I mentioned that in case of hybrid mobile apps, we have &lt;a href="https://capacitorjs.com/" rel="noopener noreferrer"&gt;Capacitor&lt;/a&gt; at hand.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To be honest, we have not only Capacitor but also &lt;a href="https://cordova.apache.org/" rel="noopener noreferrer"&gt;Cordova&lt;/a&gt; which Capacitor is based on but because Capacitor is more popular, has better community, deals with some problems better, and works beautifully with &lt;a href="https://ionicframework.com/" rel="noopener noreferrer"&gt;Ionic Framework&lt;/a&gt; I will tell more in a second, I simply recommend Capacitor.&lt;/p&gt;

&lt;p&gt;And BTW. People doooon’t like Cordova as &lt;a href="https://2022.stateofjs.com/en-US/libraries/mobile-desktop/" rel="noopener noreferrer"&gt;The State of JS 2022&lt;/a&gt; or &lt;a href="https://survey.stackoverflow.co/2022/#technology-most-loved-dreaded-and-wanted" rel="noopener noreferrer"&gt;StackOverflow Survey&lt;/a&gt; for the same year suggest so recommending Capacitor instead of Cordova looks reasonable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wn0r98sqe0bkn3zp0b4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0wn0r98sqe0bkn3zp0b4.png" alt="Almost 3/4 of the people calls Cordova " width="800" height="107"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Capacitor is a runtime environment which we can use to open a web app inside of a mobile native app. &lt;/p&gt;

&lt;p&gt;Additionally it provides plugins we can use to handle native functionality such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;geolocation,&lt;/li&gt;
&lt;li&gt;sharing,&lt;/li&gt;
&lt;li&gt;notifications,&lt;/li&gt;
&lt;li&gt;accelerometer,&lt;/li&gt;
&lt;li&gt;and many others.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a good sketch that illustrates how Capacitor works I’ve got from the article: &lt;a href="https://javascript.plainenglish.io/capacitor-turn-your-web-app-into-a-mobile-app-4d114249e55b" rel="noopener noreferrer"&gt;“CapacitorJS: Turn Your Web App into a Mobile App”&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh13apdlayzlg4nhtc4wn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh13apdlayzlg4nhtc4wn.png" alt="Capacitor provides a bridge between web app and native functionality" width="800" height="731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does it mean to us?
&lt;/h2&gt;

&lt;p&gt;It means that to make a mobile app using Capacitor, we don’t need anything more than knowledge on how to create a web app!&lt;/p&gt;

&lt;p&gt;Dealing with native functionality comes down to using simple JavaScript API. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Geolocation&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/geolocation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;printCurrentPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;coordinates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Geolocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Current position:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;coordinates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, as I mentioned, we can help ourselves with &lt;a href="https://ionicframework.com/" rel="noopener noreferrer"&gt;Ionic&lt;/a&gt; - framework which can be based on React, Vue, or Angular and which provides routing, theming, styles, and most importantly - a lot of built in components made for mobile like:&lt;/p&gt;

&lt;p&gt;Alerts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuy67vzqgtp3bewsiyslf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuy67vzqgtp3bewsiyslf.png" alt="Alerts" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Toggles:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rqojerv8bv9jtpbwk4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2rqojerv8bv9jtpbwk4g.png" alt="Toggles" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And many, many others:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76ob13awj5inirjrhcuy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F76ob13awj5inirjrhcuy.png" alt="Buttons, infinity loaders, cards, refreshers, and many, many others..." width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All of them with dedicated styles for iOS and Android.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting back to the main topic - if our app is “just a web app”, it can use micro frontends in the same way as in case of web apps!
&lt;/h2&gt;

&lt;p&gt;So we can create 2 separate micro frontends (MFs):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first MF that consumes second MF and does nothing else,&lt;/li&gt;
&lt;li&gt;and the second MF that does everything else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first MF will be then built by us and uploaded to App Store and Play Store. &lt;/p&gt;

&lt;p&gt;The second MF will be placed under some URL like &lt;code&gt;https://our-awesome-micro-frontend.com&lt;/code&gt; and retrieved by the first MF in runtime (every time the user runs our app).&lt;/p&gt;

&lt;p&gt;In this scenario, to update our mobile app, we don’t need to build it and upload to App Store and Play Store every time but just update the second, remote MF and deploy it to the server. First MF will retrieve the newest version of the second one in runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  But, is it okay for Apple and Google to basically omit their review processes?
&lt;/h2&gt;

&lt;p&gt;I’ve asked myself the same question and the answer is - they are okay with it.&lt;/p&gt;

&lt;p&gt;Micro frontends implemented in this way are treated as any other content retrieved from an external API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enough theory! Let’s check it by creating our base app and micro frontend it will consume
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;For code with everything I will show today, you can check my &lt;code&gt;[ionic-module-federation&lt;/code&gt; GitHub repository](&lt;a href="https://github.com/robert-orlinski/ionic-module-federation" rel="noopener noreferrer"&gt;https://github.com/robert-orlinski/ionic-module-federation&lt;/a&gt;)!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I will scaffold both apps using Ionic which by default uses Capacitor (both mentioned before). &lt;/p&gt;

&lt;p&gt;I will select React as a used framework but you can select Angular or Vue instead.&lt;/p&gt;

&lt;p&gt;If I choose React as a used framework, Ionic’s boilerplate is based on &lt;a href="https://create-react-app.dev/" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt;. Because of it I want a tool which edits &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;Webpack’s&lt;/a&gt; configuration without &lt;a href="https://create-react-app.dev/docs/available-scripts/#npm-run-eject" rel="noopener noreferrer"&gt;ejecting&lt;/a&gt; CRA’s configuration.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Everything because I will handle our micro frontends using Webpack’s &lt;a href="https://webpack.js.org/concepts/module-federation/" rel="noopener noreferrer"&gt;module federation&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are not familiar with module federation, you can check these videos on YouTube:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=s_Fs4AXsTnA" rel="noopener noreferrer"&gt;Micro-Frontends in Just 10 Minutes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=lKKsjpH09dU" rel="noopener noreferrer"&gt;Micro-Frontends Course - Beginner to Expert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=0WIFW3s2fDM" rel="noopener noreferrer"&gt;Five Module Federation/Micro-Frontend Mistakes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tool I will use in order to change our Webpack’s configuration is &lt;a href="https://craco.js.org/" rel="noopener noreferrer"&gt;CRACO&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s go 🎉
&lt;/h2&gt;

&lt;p&gt;We can create a new catalogue (let’s call it &lt;code&gt;ionic-module-federation&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;ionic-module-federation
&lt;span class="nb"&gt;cd &lt;/span&gt;ionic-module-federation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside of it we create 2 new projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 for our base app that will be uploaded to App Store and Google Play,&lt;/li&gt;
&lt;li&gt;and 1 for micro frontend that will be consumed by the base app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To crate them, we can install &lt;a href="https://www.npmjs.com/package/@ionic/cli" rel="noopener noreferrer"&gt;@ionic/cli&lt;/a&gt; and then run &lt;code&gt;ionic start&lt;/code&gt; for both apps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @ionic/cli
ionic start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we get through a wizard for the base app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Use the app creation wizard? No
? Framework: React
? Project name: host
? Starter template: blank
? Create free Ionic account? No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going through the options I’ve chosen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don’t want to use app creation wizard. If I did, I would be redirected to Ionic’s website, need to create an account, and my app would be automatically added to &lt;a href="https://ionic.io/appflow" rel="noopener noreferrer"&gt;Appflow’s&lt;/a&gt; account. I don’t need these.&lt;/li&gt;
&lt;li&gt;I choose React as my JS framework.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;host&lt;/code&gt; is the name of my base app.&lt;/li&gt;
&lt;li&gt;I don’t want any starter template for my base app so I choose &lt;code&gt;blank&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;And I don’t want to create Ionic’s account as I mentioned in the first point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then do the same for the micro frontend consumed by our base app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? Use the app creation wizard? No
? Framework: React
? Project name: remote
? Starter template: list
? Create free Ionic account? No
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only differences here are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The name (of course).&lt;/li&gt;
&lt;li&gt;The fact that I am using starter template. Probably it’s not required in your case but to nicely show how our base app consumes the &lt;code&gt;remote&lt;/code&gt; app I will use it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Then we land with our 2 front-end projects
&lt;/h2&gt;

&lt;p&gt;File structure looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2y2rzin3d2c1chty4r3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo2y2rzin3d2c1chty4r3.png" alt="Very similar to Create React App. Contains additional Capacitor and Ionic configs" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both of them are based on Create React App.&lt;/p&gt;

&lt;p&gt;Of course, right now we can develop our apps in a way Ionic and Capacitor let us to develop them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create web apps,&lt;/li&gt;
&lt;li&gt;handle native functionality in places we need to by using Capacitor’s plugins,&lt;/li&gt;
&lt;li&gt;build our apps for iOS and Android,&lt;/li&gt;
&lt;li&gt;test them using Xcode and Android Studio,&lt;/li&gt;
&lt;li&gt;upload them to App Store and Google Play.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But these actions are not the ones I will take right now. You can read more on them in &lt;a href="https://ionicframework.com/docs" rel="noopener noreferrer"&gt;Ionic&lt;/a&gt;, &lt;a href="https://capacitorjs.com/docs" rel="noopener noreferrer"&gt;Capacitor&lt;/a&gt;, &lt;a href="https://developer.apple.com/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, and &lt;a href="https://developers.google.com/" rel="noopener noreferrer"&gt;Google&lt;/a&gt; documentations.&lt;/p&gt;

&lt;p&gt;Right now, I want to do micro frontend stuff! 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  So let’s configure module federation for both apps!
&lt;/h2&gt;

&lt;p&gt;As I mentioned, in our case, the perfect tool for this job is &lt;a href="https://craco.js.org/" rel="noopener noreferrer"&gt;CRACO&lt;/a&gt;. It will let us simply overwrite CRA’s configuration without ejecting.&lt;/p&gt;

&lt;p&gt;To start, we can install it for both apps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;host
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @craco/craco

&lt;span class="nb"&gt;cd &lt;/span&gt;remote
npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @craco/craco
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can use &lt;a href="https://docs.npmjs.com/cli/v7/using-npm/workspaces" rel="noopener noreferrer"&gt;npm workspaces&lt;/a&gt; or &lt;a href="https://yarnpkg.com/features/workspaces" rel="noopener noreferrer"&gt;yarn workspaces&lt;/a&gt; to install it once.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And then create a file in which our configuration will be placed. File has to be called &lt;code&gt;craco.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will create it for both apps - &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;host&lt;/code&gt; our &lt;code&gt;craco.config.js&lt;/code&gt; will have such a content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModuleFederationPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack/lib/container/ModuleFederationPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;remotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;remote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remote@http://localhost:3002/remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;exposes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
          &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;http://localhost:3002&lt;/code&gt; is the address of our &lt;code&gt;remote&lt;/code&gt; app. &lt;/p&gt;

&lt;p&gt;In case you upload &lt;code&gt;remote&lt;/code&gt; app to some remote server, you will need to replace it by server’s url.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;FYI - to always open &lt;code&gt;remote&lt;/code&gt; app on port &lt;code&gt;3002&lt;/code&gt; instead of default &lt;code&gt;3000&lt;/code&gt; I change &lt;code&gt;start&lt;/code&gt; script inside of &lt;code&gt;remote&lt;/code&gt;'s &lt;code&gt;package.json&lt;/code&gt; from &lt;code&gt;react-scripts start&lt;/code&gt;  to &lt;code&gt;PORT=3002 react-scripts&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In turn, for &lt;code&gt;remote&lt;/code&gt; app &lt;code&gt;craco.config.js&lt;/code&gt; will have this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModuleFederationPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack/lib/container/ModuleFederationPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remote&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;remotes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
          &lt;span class="na"&gt;exposes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;react&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very similar. The only difference is the fact that instead of setting remotes in module federation configuration, we expose specific components. &lt;/p&gt;

&lt;h2&gt;
  
  
  Then we can use our exposed component
&lt;/h2&gt;

&lt;p&gt;In my case, I expose only one component. This is the main one (&lt;code&gt;App&lt;/code&gt;) because I want to consume whole &lt;code&gt;remote&lt;/code&gt; app basically.&lt;/p&gt;

&lt;p&gt;Then to consume this component, we can simply import it inside of the &lt;code&gt;host&lt;/code&gt; app in this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remote/App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I will do it inside of the &lt;code&gt;host/index.tsx&lt;/code&gt; file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BTW. Regarding &lt;code&gt;.tsx&lt;/code&gt; files - Ionic supports TypeScript by default and there is even no way to generate boilerplate with files written in classic JS. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ionic suggests that if for some reason you don’t want to use TS, you can simply rename all files and remove type annotations from them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;/blockquote&gt;

&lt;p&gt;Probably you’ve already spotted that TypeScript gives an error when we import our &lt;code&gt;App&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmh6anpf8fncu0vv59xa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmh6anpf8fncu0vv59xa.png" alt="" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s because module federation doesn’t contain any types.&lt;/p&gt;

&lt;p&gt;I can silence TypeScript for now by for example creating file called &lt;code&gt;remote.d.ts&lt;/code&gt; inside of our &lt;code&gt;src&lt;/code&gt; directory and put code like this inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remote/App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but of course, this is not a good solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can find better solution in the &lt;a href="https://youtu.be/0WIFW3s2fDM?t=1054" rel="noopener noreferrer"&gt;5th part of Five Module Federation/Micro-Frontend Mistakes&lt;/a&gt; video I’ve mentioned several paragraphs before.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Now we can run our app!
&lt;/h2&gt;

&lt;p&gt;But one more thing.&lt;/p&gt;

&lt;p&gt;To use CRACO, we need to change &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, and &lt;code&gt;test&lt;/code&gt; commands in &lt;code&gt;package.json&lt;/code&gt; files for both &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// host:&lt;/span&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PORT=3002 craco start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;craco build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;craco test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-scripts eject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="c1"&gt;// remote: &lt;/span&gt;

&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;craco start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;craco build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;craco test --transformIgnorePatterns 'node_modules/(?!(@ionic/react|@ionic/react-router|@ionic/core|@stencil/core|ionicons)/)'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-scripts eject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Then we can run both apps 🎉
&lt;/h2&gt;

&lt;p&gt;Let's use  &lt;code&gt;npm start&lt;/code&gt; twice - inside of a &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt; app separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;host
npm start

&lt;span class="nb"&gt;cd &lt;/span&gt;remote
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aaaaand let’s get this error in runtime: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs83pk2911pajng8wbs06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs83pk2911pajng8wbs06.png" alt="" width="800" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ouch 🤧&lt;/p&gt;

&lt;p&gt;Fortunately, there is nothing to worry about. &lt;/p&gt;

&lt;p&gt;We only need to create new file (let’s call it &lt;code&gt;src/bootstrap.tsx&lt;/code&gt; as it was adapted), copy &lt;code&gt;src/index.tsx&lt;/code&gt; content into it and change already copied &lt;code&gt;src/index.tsx&lt;/code&gt; content to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./bootstrap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can read more on the topic in “Troubleshooting” section in this article: &lt;a href="https://blog.devgenius.io/module-federation-advanced-api-inwebpack-5-0-0-beta-17-71cd4d42e534" rel="noopener noreferrer"&gt;Module Federation. Advanced API in Webpack 5.0.0-beta.17&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we restart our apps now.&lt;/p&gt;

&lt;p&gt;We should see the same content under both &lt;code&gt;[localhost:3000](http://localhost:3000/)&lt;/code&gt; (&lt;code&gt;host&lt;/code&gt;) and &lt;code&gt;[localhost:3002](http://localhost:3002/)&lt;/code&gt; (&lt;code&gt;remote&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzxs5n8jws1isng8gete.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftzxs5n8jws1isng8gete.png" alt="Our app run inside of the browser" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  That means, our module federation works! 🥳
&lt;/h2&gt;

&lt;p&gt;We can change anything inside of &lt;code&gt;remote/App&lt;/code&gt; component and see that the change is visible both on &lt;code&gt;[localhost:3000](http://localhost:3000/)&lt;/code&gt; and &lt;code&gt;[localhost:3002](http://localhost:3002/)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  And that also means we will be able to skip mobile app’s review after we upload our app to App Store and Google Play!
&lt;/h2&gt;

&lt;p&gt;We don’t have to edit anything inside of our &lt;code&gt;host&lt;/code&gt; micro frontend (one which will be then uploaded to App Store and Google Play) to update our mobile app. &lt;/p&gt;

&lt;p&gt;We can always update only &lt;code&gt;remote&lt;/code&gt; micro frontend (the same one we can host “somewhere on the internet”) and just retrieve it in runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  With one note - we can do it as long as we don’t need to add any native functionality to our mobile app
&lt;/h2&gt;

&lt;p&gt;If we want to handle stuff like geolocation, notifications, or accelerometer in &lt;code&gt;remote&lt;/code&gt;, we have to install proper Capacitor plugin both in &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;remote&lt;/code&gt; and then sync native apps.&lt;/p&gt;

&lt;p&gt;We can do it to show how it works.&lt;/p&gt;

&lt;p&gt;Let’s test &lt;a href="https://capacitorjs.com/docs/apis/share" rel="noopener noreferrer"&gt;sharing native functionality&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First of all, we can add some code in our &lt;code&gt;remote&lt;/code&gt; app that will handle content sharing.&lt;/p&gt;

&lt;p&gt;I will add very simple &lt;code&gt;button&lt;/code&gt; to the end of the &lt;code&gt;Home&lt;/code&gt; component, that will invoke sharing on click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonList&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MessageListItem&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonList&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;IonContent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;
    &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Share&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;share&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;See cool stuff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Really awesome thing you need to see right meow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://ionicframework.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dialogTitle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Share with buddies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    Share 
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonButton&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;IonPage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Share&lt;/code&gt; is imported from the &lt;code&gt;@capacitor/share&lt;/code&gt; plugin which handles sharing functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Share&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@capacitor/share&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, our &lt;code&gt;@capacitor/share&lt;/code&gt; plugin has to be installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ...in `host` app to take it into consideration while updating native platforms:&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;host
npm &lt;span class="nb"&gt;install&lt;/span&gt; @capacitor/share

&lt;span class="c"&gt;# ...and in `remote` app to import it properly and without errors:&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;remote
npm &lt;span class="nb"&gt;install&lt;/span&gt; @capacitor/share
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I can build our &lt;code&gt;host&lt;/code&gt; app and generate it for native platforms.&lt;/p&gt;

&lt;p&gt;For testing purposes, I will add only iOS platform to our Ionic project, then generate and open our app inside of XCode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;remote 
npm start &lt;span class="c"&gt;# If we want to run our `host` app after building it and consume our micro frontend, `remote` app has to operate.&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;host
ionic capacitor add ios
npm run build
ionic capacitor &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="nt"&gt;--no-build&lt;/span&gt; &lt;span class="c"&gt;# `ionic capacitor sync` builds our app using `react-scripts` so because I use CRACO, I don't use this default build to happen. I build our app using `npm run build` before running `ionic capacitor sync`.&lt;/span&gt;
ionic capacitor open ios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;ionic capacitor open ios&lt;/code&gt;, native app gets opened inside of XCode. When we run it, we see our button at the bottom of the home view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzkh67zfunjwv1ncjmk7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzkh67zfunjwv1ncjmk7.png" alt="Our app run inside of the simulator" width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then when we click on it, we can see our sharing menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfjxh27ptnf2bukw0err.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftfjxh27ptnf2bukw0err.png" alt="Our app run inside of the simulator after opening the sharing menu" width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It wouldn’t work if we added &lt;code&gt;@capacitor/share&lt;/code&gt; package only to the &lt;code&gt;remote&lt;/code&gt; app. We had to add it also to our main app - that is the &lt;code&gt;host&lt;/code&gt; app.&lt;/p&gt;

&lt;h2&gt;
  
  
  And that would be it!
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Once more, you can check &lt;code&gt;[ionic-module-federation&lt;/code&gt; repo](&lt;a href="https://github.com/robert-orlinski/ionic-module-federation" rel="noopener noreferrer"&gt;https://github.com/robert-orlinski/ionic-module-federation&lt;/a&gt;) for code created in this tutorial.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the way I’ve shown, we can deploy new version of our mobile app anytime we want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without asking Apple and Google for review.&lt;/li&gt;
&lt;li&gt;Without building and uploading our app to App Store and Google Play every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can just have 1 app which consumes remotely hosted micro frontend. That micro frontend can be changed anytime we want (assuming that we don’t add any new native functionality).&lt;/p&gt;

&lt;p&gt;I hope this tutorial was useful for you!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
