{"id":5480,"date":"2023-07-17T16:22:12","date_gmt":"2023-07-17T20:22:12","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=5480"},"modified":"2023-07-20T15:15:10","modified_gmt":"2023-07-20T19:15:10","slug":"build-your-own-app-router-in-capacitor-apps-with-vanillajs","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs","title":{"rendered":"Build your own App Router in Capacitor Apps with VanillaJS"},"content":{"rendered":"\n<p><em>This is a guest post from Simon Grimm, Ionic Developer Expert and educator at <a href=\"https:\/\/ionicacademy.com\/\">the Ionic Academy<\/a>, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor!<\/em><\/p>\n\n\n\n<p>When building apps with Capacitor, you usually don\u2019t worry a lot about routing as frameworks like Angular, React or Vue have their own routing solutions. But what if you simply want to <a href=\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\">build a great Capacitor app with VanillaJS<\/a>?<\/p>\n\n\n\n<p>In that case you have to roll your own routing logic, but spoiler: It\u2019s actually not that hard!<\/p>\n\n\n\n<p>In this tutorial, I\u2019ll show you how to implement your own router for Capacitor apps with VanillaJS, and you can also find the full <a href=\"http:\/\/localhost:8470\/page\/(https:\/\/github.com\/saimon24\/capacitor-vanilla-routing)\">Capacitor VanillaJS routing code on Github<\/a>.<\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Creating a Capacitor App<\/h2>\n\n\n\n<p>Since we are not using any framework in this tutorial, we can directly scaffold a basic Capacitor app through the command line:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm init @capacitor\/app<\/code><\/pre>\n\n\n\n<p>Answer the questions to give your app a name, then install all dependencies and immediately run your app on the browser:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install\nnpm run start<\/code><\/pre>\n\n\n\n<p>This should start a Vite development server and your app is served at <a href=\"http:\/\/localhost:3000\/\">http:\/\/localhost:3000\/<\/a> with some basic elements.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh6.googleusercontent.com\/6dzjyVe5RizfjWoPPmNINLly8aZVLQ9uhhlkRMMhsBLJUZ9tb1qYmQ2qlLU6BiwmpbTCpP6GfL8R62Z_1nTGe-rlNSRJDppahdAtqsOuSVEbZpZ2jbSVyTQSMUY60hY20cZ0lJVH5A1vmQ7pCQZ8z7UQP0iKTnXIuo21m_JmP10GS3aBdBemViDteKTXTw\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/6dzjyVe5RizfjWoPPmNINLly8aZVLQ9uhhlkRMMhsBLJUZ9tb1qYmQ2qlLU6BiwmpbTCpP6GfL8R62Z_1nTGe-rlNSRJDppahdAtqsOuSVEbZpZ2jbSVyTQSMUY60hY20cZ0lJVH5A1vmQ7pCQZ8z7UQP0iKTnXIuo21m_JmP10GS3aBdBemViDteKTXTw\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>Although we want the least amount of packages, <a href=\"https:\/\/tailwindcss.com\/\">TailwindCSS<\/a> is one of the essential packages nowadays when it comes to styling and utility libraries, so let\u2019s add this to our app.<\/p>\n\n\n\n<p>However, note that this is not required to build our routing &#8211; it just makes our Capacitor app look awesome.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding TailwindCSS<\/h2>\n\n\n\n<p>You can style your web app with just CSS, but throwing in TailwindCSS makes it easy to have a great-looking app in minutes, still without committing to any JS framework.<\/p>\n\n\n\n<p>To do so, simply install it as described:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install -D tailwindcss postcss autoprefixer\nnpx tailwindcss init<\/code><\/pre>\n\n\n\n<p>Now we just need to make sure we are including our files in the <code>tailwind.config.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">\/** @type {import(&#039;tailwindcss&#039;).Config} *\/\nmodule.exports = {\n\u00a0\u00a0content: [&quot;.\/src\/**\/*.{html,js}&quot;],\n\u00a0\u00a0theme: {\n\u00a0\u00a0\u00a0\u00a0extend: {},\n\u00a0\u00a0},\n\u00a0\u00a0plugins: [],\n};<\/code><\/pre>\n\n\n\n<p>Additionally, we need to use PostCSS and have to create a new postcss.config.js at the root of our project with the following content:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">module.exports = {\n\u00a0\u00a0plugins: {\n\u00a0\u00a0\u00a0\u00a0tailwindcss: {},\n\u00a0\u00a0\u00a0\u00a0autoprefixer: {},\n\u00a0\u00a0},\n};<\/code><\/pre>\n\n\n\n<p>As a last step we need to load the Tailwind classes, so let\u2019s add the following to our <code>src\/css\/style.css<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-css\">\/* Add Tailwind imports *\/\n@tailwind base;\n@tailwind components;\n@tailwind utilities;<\/code><\/pre>\n\n\n\n<p>You can confirm that everything works correctly by adding some Tailwind utility classes to an element in your:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;h1 class=&quot;m-4 font-bold text-4xl underline&quot;&gt;TEST&lt;\/h1&gt;<\/code><\/pre>\n\n\n\n<p>This should result in a styled header element, and you should restart your live reload after all previous steps have been completed.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh4.googleusercontent.com\/KjmOjwFEvGjNwe7FMsohhytJGLHXpvsKPsVfcyi-kvfxsVOXhLomZrI9rBh3wDUqGjOfepbs0stiPRAM2BquXNcRO43mjYmrG4X1eyrWSLFsR7XFz15W0v_mL3-YME35CKwj8agrIwTEgnvrcSXmQ540Wo0LsfJBxw0TgWf2lbwyvZRXAt_sUghsaxrjmw\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh4.googleusercontent.com\/KjmOjwFEvGjNwe7FMsohhytJGLHXpvsKPsVfcyi-kvfxsVOXhLomZrI9rBh3wDUqGjOfepbs0stiPRAM2BquXNcRO43mjYmrG4X1eyrWSLFsR7XFz15W0v_mL3-YME35CKwj8agrIwTEgnvrcSXmQ540Wo0LsfJBxw0TgWf2lbwyvZRXAt_sUghsaxrjmw\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>Now we are ready to use Capacitor with VanillaJS to create powerful native mobile apps with Tailwind styling!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Building a Capacitor App with Routing<\/h2>\n\n\n\n<p>By default, the Capacitor app has one Javascript file that defines a custom element with some styling and functionality.<\/p>\n\n\n\n<p>As we want to build our own functionality from the ground up, let\u2019s delete the <code>src\/js\/capacitor-welcome.js<\/code> and also all references to it inside the <code>src\/index.html<\/code>.<\/p>\n\n\n\n<p>Now let\u2019s approach our Vanilla routing!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Routing in VanillaJS Apps<\/h2>\n\n\n\n<p>When it comes to routing in VanillaJS apps, there are many different approaches and libraries out there.<\/p>\n\n\n\n<p>The most basic way is to use the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/History_API\">History API<\/a> to change the URL and listen to changes, but this is not very convenient.<\/p>\n\n\n\n<p>Instead we will use a super slim package called <a href=\"https:\/\/github.com\/krasimir\/navigo\">Navigo<\/a> that will give us some additional methods and ways to listen to changes.<\/p>\n\n\n\n<p>If you are adventurous, feel free to try and <a href=\"https:\/\/blog.skay.dev\/custom-spa-router-vanillajs\">build your own SPA router<\/a>!<\/p>\n\n\n\n<p>To get started, let\u2019s install Navigo:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install navigo<\/code><\/pre>\n\n\n\n<p>Now we can define our different routes and also the corresponding components that should be rendered with Navigo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating a Simple Tab Bar<\/h2>\n\n\n\n<p>To use Navigo we need to set up a router instance and define our routes, and then use <strong>data-navigo<\/strong> attributes on our links to make them work.<\/p>\n\n\n\n<p>Let\u2019s start by creating a new <code>src\/js\/router.js<\/code> file and add the following code:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">import Navigo from &quot;navigo&quot;;\n\nconst router = new Navigo(&quot;\/&quot;);\n\nconst render = (content) =&gt; (document.querySelector(&quot;#content&quot;).innerHTML = content);\n\nrouter\n  .on(&quot;\/&quot;, (match) =&gt; {\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;h1&gt;Home&lt;\/h1&gt;&lt;\/div&gt;`);\n  })\n  .on(&quot;\/posts&quot;, (match) =&gt; {\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;div class=&quot;posts&quot;&gt;\n    &lt;a href=&quot;\/posts\/1&quot; data-navigo&gt;Post 1&lt;\/a&gt;\n    &lt;a href=&quot;\/posts\/2&quot; data-navigo&gt;Post 2&lt;\/a&gt;\n    &lt;a href=&quot;\/posts\/3&quot; data-navigo&gt;Post 3&lt;\/a&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;`);\n  })\n  .on(&quot;\/settings&quot;, (match) =&gt; {\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;h1&gt;Settings&lt;\/h1&gt;&lt;\/div&gt;`);\n  })\n  .resolve();<\/code><\/pre>\n\n\n\n<p>This code will create a new Navigo router instance, and then define three routes for the home page, the posts page, and the settings page.<\/p>\n\n\n\n<p>Additionally, we are using a render function that will simply set the inner HTML of our #content element to the content we pass in.<\/p>\n\n\n\n<p>Finally, we are calling the resolve method to make sure that the current route is resolved and the correct content is rendered.This means that our actual content lives in one div and is simply swapped out when the URL changes. This basic approach works well for our use case.<\/p>\n\n\n\n<p>You could make this more advanced by adding a templating engine like <a href=\"https:\/\/handlebarsjs.com\/\">Handlebars<\/a> or <a href=\"https:\/\/ejs.co\/\">EJS<\/a> to render the content from files, which is certainly required for more complex projects.<\/p>\n\n\n\n<p>Now we can add the following code to our src\/index.html to define some tabs and make them work with Navigo:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;body&gt;\n    &lt;div class=&quot;flex flex-col min-h-screen&quot;&gt;\n      &lt;div class=&quot;app-navbar-notch&quot;&gt;&lt;\/div&gt;\n\n      &lt;div id=&quot;content&quot; class=&quot;flex-1&quot;&gt;&lt;\/div&gt;\n\n      &lt;div class=&quot;tab-bar&quot;&gt;\n        &lt;nav class=&quot;flex&quot; aria-label=&quot;Tabs&quot;&gt;\n          &lt;a href=&quot;\/&quot; data-navigo class=&quot;tab&quot;&gt;Home&lt;\/a&gt;\n          &lt;a href=&quot;\/posts&quot; data-navigo class=&quot;tab&quot;&gt;Posts&lt;\/a&gt;\n          &lt;a href=&quot;\/settings&quot; data-navigo class=&quot;tab&quot;&gt;Settings&lt;\/a&gt;\n        &lt;\/nav&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;script src=&quot;.\/js\/router.js&quot; type=&quot;module&quot;&gt;&lt;\/script&gt;\n  &lt;\/body&gt;<\/code><\/pre>\n\n\n\n<p>At this point it won\u2019t really look like tabs, so let\u2019s sprinkle in some Tailwind styling through our <code>src\/css\/style.css<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-css\">:root {\n  --nav-bg: theme(colors.blue.600);\n}\n\n.tab-bar {\n  @apply border-t border-gray-200 w-full fixed bottom-0;\n}\n\n.tab {\n  @apply flex-1 flex justify-center whitespace-nowrap py-4 px-1 text-sm font-medium;\n  margin-bottom: env(safe-area-inset-bottom);\n}\n<\/code><\/pre>\n\n\n\n<p>This will give us a nice tab bar at the bottom of our app, and we can now navigate between the different pages.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh6.googleusercontent.com\/EadfDAa56fsWTFoCmyb0mijXPQqUU86Zfm5FtU6SEv7iYVIynHal_h_DkeLp7AlVkJxoEnaQH3OeKV5r33z6eTXjbEOqmI8C9_EoEt-m92v4nU6mcbp-GdE0dImyrLlfxnmKlRxBUimNdBl9pZoOgL1lBh32HC5bIL-ec6WpzxnHlfnaIab_yv775ivqog\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/EadfDAa56fsWTFoCmyb0mijXPQqUU86Zfm5FtU6SEv7iYVIynHal_h_DkeLp7AlVkJxoEnaQH3OeKV5r33z6eTXjbEOqmI8C9_EoEt-m92v4nU6mcbp-GdE0dImyrLlfxnmKlRxBUimNdBl9pZoOgL1lBh32HC5bIL-ec6WpzxnHlfnaIab_yv775ivqog\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>But this is far from a real tab bar, so let\u2019s add some additional functionality to make it more dynamic.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tab Changes and Dynamic URLs<\/h2>\n\n\n\n<p>It\u2019s not enough to render the content of a selected tab, we also want to show which tab is active. Additionally, let\u2019s also add one more route to Navigo to show a single post based on an ID in the URL.<\/p>\n\n\n\n<p>Therefore we can update our <code>src\/js\/router.js<\/code> to the following:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">import Navigo from &quot;navigo&quot;;\n\nconst router = new Navigo(&quot;\/&quot;);\nconst body = document.querySelector(&quot;body&quot;);\n\nconst render = (content) =&gt; (document.querySelector(&quot;#content&quot;).innerHTML = content);\n\nrouter\n  .on(&quot;\/&quot;, (match) =&gt; {\n    body.dispatchEvent(new CustomEvent(&quot;page-changed&quot;, { detail: { page: &quot;Home&quot;, tab: 0 } }));\n    updateTabbar(0);\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;h1&gt;Home&lt;\/h1&gt;&lt;\/div&gt;`);\n  })\n  .on(&quot;\/posts&quot;, (match) =&gt; {\n    body.dispatchEvent(new CustomEvent(&quot;page-changed&quot;, { detail: { page: &quot;Posts&quot;, tab: 1 } }));\n    updateTabbar(1);\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;div class=&quot;posts&quot;&gt;\n    &lt;a href=&quot;\/posts\/1&quot; data-navigo&gt;Post 1&lt;\/a&gt;\n    &lt;a href=&quot;\/posts\/2&quot; data-navigo&gt;Post 2&lt;\/a&gt;\n    &lt;a href=&quot;\/posts\/3&quot; data-navigo&gt;Post 3&lt;\/a&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;`);\n  })\n  .on(&quot;\/settings&quot;, (match) =&gt; {\n    body.dispatchEvent(new CustomEvent(&quot;page-changed&quot;, { detail: { page: &quot;Settings&quot;, tab: 2 } }));\n    updateTabbar(2);\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;h1&gt;Settings&lt;\/h1&gt;&lt;\/div&gt;`);\n  })\n  .on(&quot;\/posts\/:id&quot;, (match) =&gt; {\n    console.log(match);\n    const id = match.data.id;\n    body.dispatchEvent(new CustomEvent(&quot;page-changed&quot;, { detail: { page: `Post ${id}`, tab: 1, back: true } }));\n    updateTabbar(1);\n    render(`&lt;div class=&quot;page&quot;&gt;&lt;h1&gt;POST DETAILS: ${id}&lt;\/h1&gt;&lt;\/div&gt;`);\n  })\n  .resolve();\n\nfunction updateTabbar(activeIndex) {\n  const tabs = document.getElementsByClassName(&quot;tab&quot;);\n\n  for (let i = 0; i &lt; tabs.length; i++) {\n    if (i === activeIndex) {\n      tabs[i].classList.add(&quot;tab-active&quot;);\n    } else {\n      tabs[i].classList.remove(&quot;tab-active&quot;);\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>We basically enriched our routes with some additional functionality:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We are now dispatching a <strong>page-changed<\/strong> event on the body element whenever a route is resolved. This event contains the name of the page and the index of the tab that should be active &#8211; we will use this soon to include a fancy additional navigation bar at the top of our app.<\/li>\n\n\n\n<li>We are also calling a new <strong>updateTabbar<\/strong> function that will update the tab bar CSS class based on the active index.<\/li>\n\n\n\n<li>We are also adding a new route for <strong>\/posts\/:id<\/strong> that will show a single post based on the ID in the URL. This route will also dispatch the page-changed event, but with a back property set to true. This will be used to show a back button in the tab bar.<\/li>\n<\/ul>\n\n\n\n<p>Now that we are adding additional classes to the tabs, let\u2019s define the styling for those in our <code>src\/css\/style.css<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-css\">.tab-active {\n  @apply border-t-2 border-blue-600 text-blue-600 flex-1 flex justify-center whitespace-nowrap py-4 px-1 text-sm font-medium;\n}\n\n.page {\n  height: calc(100vh - 54px - 53px - env(safe-area-inset-top) - env(safe-area-inset-bottom));\n  margin-top: env(safe-area-inset-top);\n}\n\n.posts {\n  @apply p-4 flex flex-col gap-4;\n}\n\n.posts a {\n  @apply border-gray-200 border-2 rounded-md p-4 flex flex-col gap-4 shadow-sm;\n}<\/code><\/pre>\n\n\n\n<p>This will make the active tab look a bit different from the others, and we can already navigate to a details page and see the ID reflected in the component:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh6.googleusercontent.com\/b9C7_8BthUlMRRH4KdMGsOLPqbehGGlRph9VmeE8D4izgxElzFJiUE7S2q6kelZXSKTEuCtcmnpaZtb_dF2GyasWZ0OpgD6kCvzL98F0UkENRqOI9ekdYoE3oKlgps0iAaTbTevwSyhhr_KalUtCcfJ12LNi3aiS4dKtxobiRxVSGPBYfFCH5A-65n0kQQ\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh6.googleusercontent.com\/b9C7_8BthUlMRRH4KdMGsOLPqbehGGlRph9VmeE8D4izgxElzFJiUE7S2q6kelZXSKTEuCtcmnpaZtb_dF2GyasWZ0OpgD6kCvzL98F0UkENRqOI9ekdYoE3oKlgps0iAaTbTevwSyhhr_KalUtCcfJ12LNi3aiS4dKtxobiRxVSGPBYfFCH5A-65n0kQQ\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>But real apps also have a navigation bar, and we can recreate this feature with VanillaJS as well.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding a Navigation Bar<\/h2>\n\n\n\n<p>The navigation bar is a common pattern in mobile apps, and it\u2019s a great way to show the current page and provide a back button to the previous page in case we navigated from a list to a details page. The navigation bar should be at the top and show the title of the current active page, and needs to have an optional back button. As we already have the page-changed event, we can use this to update the navigation bar whenever the page changes!<\/p>\n\n\n\n<p>We can create this component as a new web component, so let\u2019s add a new file <code>src\/js\/navbar.js<\/code> and insert:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">window.customElements.define(\n  &quot;custom-navbar&quot;,\n  class extends HTMLElement {\n    constructor() {\n      super();\n      const root = this.attachShadow({ mode: &quot;open&quot; });\n      root.innerHTML = `\n    &lt;style&gt;\n      :host {\n        position: relative;\n        display: block;\n        padding: 15px 15px 15px 15px;\n        text-align: center;\n        background-color: var(--nav-bg);\n        position: fixed;\n        width: 100%;\n        top: env(safe-area-inset-top);\n      }\n      .nav {\n        display: flex;\n        flex-direction: row;\n        gap: 10px;\n      }\n      h1 {\n        margin: 0;\n        font-size: 1em;\n        font-weight: 700;\n        color: #fff;\n      }\n      #back-btn {\n        width: 24px;\n        height: 24px;\n        display: none;\n        color: #fff;\n        font-size: 1em;\n        font-weight: 700;\n        text-decoration: none;\n      }\n    &lt;\/style&gt;\n    &lt;div class=&quot;nav&quot;&gt;\n      &lt;a id=&quot;back-btn&quot;&gt;\n      &lt;svg xmlns=&quot;http:\/\/www.w3.org\/2000\/svg&quot; class=&quot;ionicon&quot; viewBox=&quot;0 0 512 512&quot;&gt;&lt;path fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; stroke-width=&quot;48&quot; d=&quot;M328 112L184 256l144 144&quot;\/&gt;&lt;\/svg&gt;\n      &lt;\/a&gt;\n      &lt;div&gt;&lt;h1 id=&quot;nav-title&quot;&gt;&lt;\/h2&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n    `;\n    }\n\n    \/\/ Called on start\n    connectedCallback() {\n      const self = this;\n\n      \/\/ Listen to our page change events\n      const body = document.querySelector(&quot;body&quot;);\n      const navTitle = self.shadowRoot.getElementById(&quot;nav-title&quot;);\n\n      body.addEventListener(&quot;page-changed&quot;, (event) =&gt; {\n        const title = event.detail.page;\n        navTitle.innerHTML = title;\n\n        const canGoBack = event.detail.back;\n        if (canGoBack) {\n          self.shadowRoot.getElementById(&quot;back-btn&quot;).style.display = &quot;block&quot;;\n        } else {\n          self.shadowRoot.getElementById(&quot;back-btn&quot;).style.display = &quot;none&quot;;\n        }\n      });\n\n      const backBtn = self.shadowRoot.getElementById(&quot;back-btn&quot;);\n      backBtn.addEventListener(&quot;click&quot;, (event) =&gt; {\n        history.back();\n      });\n    }\n  }\n);\n<\/code><\/pre>\n\n\n\n<p>If you are not familiar with web components, this might look a bit strange, butit\u2019s quite simple:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>We are defining a new web component called custom-navbar that extends the <code>HTMLElement<\/code> class.<\/li>\n\n\n\n<li>We are using the <code>connectedCallback<\/code> method to listen to the <code>page-changed<\/code> event on the body element. Whenever this event is fired, we are updating the title of the navigation bar and showing or hiding the back button based on the back property of the event.<\/li>\n\n\n\n<li>We are also listening to the click event on the back button and calling <code>history.back()<\/code> to go back to the previous page.<\/li>\n\n\n\n<li>We are using a <code>shadowRoot<\/code> to encapsulate the styling of the component.<\/li>\n<\/ul>\n\n\n\n<p>You can also <a href=\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\">learn more about building Capacitor apps with VanillaJS<\/a> in one of my previous tutorials.<\/p>\n\n\n\n<p>Now that we have our component, let\u2019s add it to our <code>src\/index.html<\/code> and include a little fix from the previous tutorial to make the tab bar appear below the notch area:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;body&gt;\n    &lt;div class=&quot;flex flex-col min-h-screen&quot;&gt;\n      &lt;div class=&quot;app-navbar-notch&quot;&gt;&lt;\/div&gt;\n      &lt;div style=&quot;height: 54px&quot;&gt;\n        &lt;custom-navbar&gt;&lt;\/custom-navbar&gt;\n      &lt;\/div&gt;\n\n      &lt;div id=&quot;content&quot; class=&quot;flex-1&quot;&gt;&lt;\/div&gt;\n\n      &lt;div class=&quot;tab-bar&quot;&gt;\n        &lt;nav class=&quot;flex&quot; aria-label=&quot;Tabs&quot;&gt;\n          &lt;a href=&quot;\/&quot; data-navigo class=&quot;tab&quot;&gt;Home&lt;\/a&gt;\n          &lt;a href=&quot;\/posts&quot; data-navigo class=&quot;tab&quot;&gt;Posts&lt;\/a&gt;\n          &lt;a href=&quot;\/settings&quot; data-navigo class=&quot;tab&quot;&gt;Settings&lt;\/a&gt;\n        &lt;\/nav&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n\n    &lt;script src=&quot;.\/js\/navbar.js&quot; type=&quot;module&quot;&gt;&lt;\/script&gt;\n    &lt;script src=&quot;.\/js\/router.js&quot; type=&quot;module&quot;&gt;&lt;\/script&gt;\n  &lt;\/body&gt;<\/code><\/pre>\n\n\n\n<p>The code for the notch area fix needs to be added to our <code>src\/css\/app.css<\/code><strong>:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-css\">\/* Fix for iOS area above our custom nav bar *\/\n.app-navbar-notch {\n  position: fixed;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: 99;\n  height: env(safe-area-inset-top);\n  @apply bg-blue-600;\n}<\/code><\/pre>\n\n\n\n<p>With all of that in place, navigate around the tabs and see how the title changes &#8211; and also check out this beauty of a details page with custom navigation bar:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh3.googleusercontent.com\/iztFJVgJBAscfIgutn3Hh3KEzrkkaNuAzcr2IJGGUCZNhEAoa2pVYK7szgyKgD6VA43hYx7c2xGZj54wWd8ixY5RltUjGUoGQA_gLdrG5ywKBnKZZjNvgM3VXdyzENHGB8pTkPk-TpqY415w6dIeOW1Dz_OMqlQYetEf1W3r55ZNjbxKCP7E07tug3ODaw\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/iztFJVgJBAscfIgutn3Hh3KEzrkkaNuAzcr2IJGGUCZNhEAoa2pVYK7szgyKgD6VA43hYx7c2xGZj54wWd8ixY5RltUjGUoGQA_gLdrG5ywKBnKZZjNvgM3VXdyzENHGB8pTkPk-TpqY415w6dIeOW1Dz_OMqlQYetEf1W3r55ZNjbxKCP7E07tug3ODaw\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>If you think this is a powerful native app, hold your praise &#8211; there\u2019s one more thing we want to add!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Capacitor Deeplinks<\/h2>\n\n\n\n<p>As we have a list of posts and detailed pages, wouldn\u2019t it be nice if we could directly open a page of our app from a link?<\/p>\n\n\n\n<p>This is possible with deeplinks &#8211; and Capacitor has a <a href=\"https:\/\/capacitorjs.com\/docs\/apis\/app\">plugin for that<\/a>! Let\u2019s install the plugin:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install @capacitor\/app<\/code><\/pre>\n\n\n\n<p>Now all we have to do in our code is to listen to the <code>appUrlOpen<\/code> event and navigate to the page that was opened.<\/p>\n\n\n\n<p>We can split the URL by a specific URL scheme, which we later also need to set up for iOS and Android (it\u2019s super easy), and then grab the right path to our post.<\/p>\n\n\n\n<p>Go ahead by adding the following to our <code>src\/js\/router.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">import { App } from &quot;@capacitor\/app&quot;;\n\nApp.addListener(&quot;appUrlOpen&quot;, (event) =&gt; {\n  const pathArray = event.url.split(&quot;capacitorvanilla:\/\/&quot;);\n  if (pathArray.length &gt; 1) {\n    const url = pathArray.pop();\n    router.navigate(url);\n  }\n});<\/code><\/pre>\n\n\n\n<p>To see that code in action we now need to move our Capacitor project to a native platform.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding Native Platforms<\/h2>\n\n\n\n<p>To get started, install the native platforms and run a build of your web project. Afterward we can add the platforms using the <a href=\"https:\/\/capacitorjs.com\/docs\/cli\">Capacitor CLI<\/a> in our project:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install @capacitor\/android @capacitor\/ios\nnpm run build\n\nnpx cap add android\nnpx cap add ios<\/code><\/pre>\n\n\n\n<p>Now whenever you want to build a native app, you can simply build your web project and sync those changes into the native folders:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm run build\nnpx cap sync<\/code><\/pre>\n\n\n\n<p>Next we need to add our URL scheme to the native platforms, so that they know how to open our app.<\/p>\n\n\n\n<p>We start with iOS, for which we can edit the <code>ios\/App\/App\/Info.plist<\/code> and add a new entry like this:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;key&gt;CFBundleURLTypes&lt;\/key&gt;\n\t&lt;array&gt;\n\t\t&lt;dict&gt;\n\t\t\t&lt;key&gt;CFBundleURLName&lt;\/key&gt;\n\t\t\t&lt;string&gt;com.galaxies.routing&lt;\/string&gt;\n\t\t\t&lt;key&gt;CFBundleURLSchemes&lt;\/key&gt;\n\t\t\t&lt;array&gt;\n\t\t\t\t&lt;string&gt;capacitorvanilla&lt;\/string&gt;\n\t\t\t&lt;\/array&gt;\n\t\t&lt;\/dict&gt;\n\t&lt;\/array&gt;<\/code><\/pre>\n\n\n\n<p>This tells iOS that our app can be opened with the <code>capacitorvanilla<\/code> URL scheme.<\/p>\n\n\n\n<p>For Android, we now need to edit the <code>android\/app\/src\/main\/AndroidManifest.xml<\/code> and add the following intent filter to the <code>MainActivity<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;intent-filter&gt;\n  &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n  &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n  &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n  &lt;data android:scheme=&quot;@string\/custom_url_scheme&quot; \/&gt;\n&lt;\/intent-filter&gt;<\/code><\/pre>\n\n\n\n<p>Additionally, we now need to add the <code>custom_url_scheme<\/code> to our <code>android\/app\/src\/main\/res\/values\/strings.xml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;string name=&quot;custom_url_scheme&quot;&gt;capacitorvanilla&lt;\/string&gt;<\/code><\/pre>\n\n\n\n<p>Now we can build our native apps and run them on a device or emulator:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npx cap run ios\nnpx cap run android<\/code><\/pre>\n\n\n\n<p>To check if this works, simply create a note somewhere on your phone and add the link <code>capacitorvanilla:\/\/posts\/1337<\/code> and open it. You should now see the details page of the post with the ID 1337!<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" data-src=\"https:\/\/lh3.googleusercontent.com\/-bzksjrgA2qZ9-pnbLHk2GH69gN4lkWDkJVXvvkrkqwKGsbEYl6yXZ9ZBGXAfkXglmGL0bMRnptCmSBaO6ruG3WPVkXGebljePDt3Qek4kouvFxVr5sEpwgMWxLv5iQDL8aVm0UtAA_vV9zCAxpAHKTKcNDoVYdL2N817zw7vi6rblcMPQS1gjvzDjtU4w\" alt=\"\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" class=\"lazyload\" \/><noscript><img decoding=\"async\" src=\"https:\/\/lh3.googleusercontent.com\/-bzksjrgA2qZ9-pnbLHk2GH69gN4lkWDkJVXvvkrkqwKGsbEYl6yXZ9ZBGXAfkXglmGL0bMRnptCmSBaO6ruG3WPVkXGebljePDt3Qek4kouvFxVr5sEpwgMWxLv5iQDL8aVm0UtAA_vV9zCAxpAHKTKcNDoVYdL2N817zw7vi6rblcMPQS1gjvzDjtU4w\" alt=\"\"\/><\/noscript><\/figure>\n\n\n\n<p>Capacitor almost makes it too easy to build native apps, right?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>&nbsp;Parting Thoughts<\/strong><\/h2>\n\n\n\n<p>In this tutorial we have learned how to add routing to a Capacitor app with just VanillaJS and Navigo.<\/p>\n\n\n\n<p>We also learned how to add deeplinks to our app, so that we can open specific pages from a link.<\/p>\n\n\n\n<p>Finally, we made everything look nice with a custom navigation bar and Tailwind styling.<\/p>\n\n\n\n<p>If you enjoyed this tutorial, check out how you can use Capacitor to build native <a href=\"https:\/\/ionic.io\/blog\/building-and-releasing-your-capacitor-ios-app\">iOS<\/a> and <a href=\"https:\/\/ionic.io\/blog\/building-and-releasing-your-capacitor-android-app\">Android<\/a> apps with the web.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is a guest post from Simon Grimm, Ionic Developer Expert and educator at the Ionic Academy, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor! When building apps with Capacitor, you usually don\u2019t worry a lot about routing as frameworks like Angular, React or Vue [&hellip;]<\/p>\n","protected":false},"author":11,"featured_media":5481,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"1","publish_post_category":"26","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"565893","discourse_permalink":"http:\/\/forum.ionicframework.com\/t\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\/234937","wpdc_publishing_response":"success","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[124],"tags":[151,273],"class_list":["post-5480","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tutorials","tag-capacitor","tag-vanillajs"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Build your own App Router in Capacitor Apps with VanillaJS - Ionic Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Build your own App Router in Capacitor Apps with VanillaJS\" \/>\n<meta property=\"og:description\" content=\"This is a guest post from Simon Grimm, Ionic Developer Expert and educator at the Ionic Academy, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor! When building apps with Capacitor, you usually don\u2019t worry a lot about routing as frameworks like Angular, React or Vue [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-07-17T20:22:12+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-07-20T19:15:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2240\" \/>\n\t<meta property=\"og:image:height\" content=\"1120\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Simon Grimm\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@schlimmson\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Simon Grimm\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\"},\"author\":{\"name\":\"Simon Grimm\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6\"},\"headline\":\"Build your own App Router in Capacitor Apps with VanillaJS\",\"datePublished\":\"2023-07-17T20:22:12+00:00\",\"dateModified\":\"2023-07-20T19:15:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\"},\"wordCount\":1817,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png\",\"keywords\":[\"Capacitor\",\"VanillaJS\"],\"articleSection\":[\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\",\"url\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\",\"name\":\"Build your own App Router in Capacitor Apps with VanillaJS - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png\",\"datePublished\":\"2023-07-17T20:22:12+00:00\",\"dateModified\":\"2023-07-20T19:15:10+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png\",\"width\":2240,\"height\":1120},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Build your own App Router in Capacitor Apps with VanillaJS\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6\",\"name\":\"Simon Grimm\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg\",\"caption\":\"Simon Grimm\"},\"sameAs\":[\"https:\/\/x.com\/schlimmson\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/schlimmson\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build your own App Router in Capacitor Apps with VanillaJS - Ionic Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs","og_locale":"en_US","og_type":"article","og_title":"Build your own App Router in Capacitor Apps with VanillaJS","og_description":"This is a guest post from Simon Grimm, Ionic Developer Expert and educator at the Ionic Academy, an online school with 70+ video courses focused entirely on building awesome mobile apps with Ionic and Capacitor! When building apps with Capacitor, you usually don\u2019t worry a lot about routing as frameworks like Angular, React or Vue [&hellip;]","og_url":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs","og_site_name":"Ionic Blog","article_published_time":"2023-07-17T20:22:12+00:00","article_modified_time":"2023-07-20T19:15:10+00:00","og_image":[{"width":2240,"height":1120,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","type":"image\/png"}],"author":"Simon Grimm","twitter_card":"summary_large_image","twitter_creator":"@schlimmson","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Simon Grimm"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs"},"author":{"name":"Simon Grimm","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6"},"headline":"Build your own App Router in Capacitor Apps with VanillaJS","datePublished":"2023-07-17T20:22:12+00:00","dateModified":"2023-07-20T19:15:10+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs"},"wordCount":1817,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","keywords":["Capacitor","VanillaJS"],"articleSection":["Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs","url":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs","name":"Build your own App Router in Capacitor Apps with VanillaJS - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","datePublished":"2023-07-17T20:22:12+00:00","dateModified":"2023-07-20T19:15:10+00:00","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","width":2240,"height":1120},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/build-your-own-app-router-in-capacitor-apps-with-vanillajs#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Build your own App Router in Capacitor Apps with VanillaJS"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6","name":"Simon Grimm","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/08\/simon-150x150.jpg","caption":"Simon Grimm"},"sameAs":["https:\/\/x.com\/schlimmson"],"url":"https:\/\/ionic.io\/blog\/author\/schlimmson"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/07\/AppRouter-feature-image-2.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5480","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=5480"}],"version-history":[{"count":3,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5480\/revisions"}],"predecessor-version":[{"id":5515,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5480\/revisions\/5515"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/5481"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=5480"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=5480"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=5480"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}