{"id":5137,"date":"2023-04-27T10:58:28","date_gmt":"2023-04-27T14:58:28","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=5137"},"modified":"2023-04-27T10:58:30","modified_gmt":"2023-04-27T14:58:30","slug":"create-powerful-native-mobile-apps-with-capacitor-vanillajs","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs","title":{"rendered":"Create Powerful Native Mobile Apps with Capacitor &#038; 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.<\/em><\/p>\n\n\n\n<p><a href=\"https:\/\/capacitorjs.com\/\">CapacitorJS<\/a> is an open-source framework that allows developers to build cross-platform mobile apps using web technologies. It provides a set of APIs and plugins to access native functionality and device features that are not typically available in web applications, such as the camera or File system.<\/p>\n\n\n\n<p>With Capacitor, every web developer can create high-performance native mobile apps for iOS and Android using familiar web development tools and workflows, without having to learn a completely new language.<\/p>\n\n\n\n<p>While Capacitor can be used with popular JavaScript frameworks like <a href=\"https:\/\/capacitorjs.com\/solution\/angular\">Angular<\/a> and <a href=\"https:\/\/capacitorjs.com\/solution\/react\">React<\/a>, it is also possible to use it with VanillaJS. In this tutorial, we will learn how to use Capacitor with VanillaJS to create powerful native iOS and Android apps.<\/p>\n\n\n\n<!--more-->\n\n\n\n<p>By using VanillaJS, we can keep our codebase lightweight and avoid the complexity that comes with larger frameworks. This makes it easier to maintain, update and scale our application over time &#8211; and you don&#8217;t need to fear the release of the next trendy JavaScript framework.<\/p>\n\n\n\n<p>By the end of this tutorial, you\u2019ll be able to get a simple photo storage app up and running &#8211; no native skills required. Thanks to Capacitor\u2019s integration with device features and VanillaJS\u2019s simple learning curve, you can create a fully functional, clean looking app in just minutes<strong>.&nbsp;<\/strong><\/p>\n\n\n\n<p>You can also take a look at the <a href=\"https:\/\/github.com\/saimon24\/capacitor-vanilla-js-tailwind\">Capacitor VanillaJS app on Github<\/a>!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Getting Started<\/strong><\/h2>\n\n\n\n<p>Before we dive into creating a simple Capacitor app with VanillaJS, I highly recommend you check out this great guide containing <a href=\"https:\/\/ionic.io\/blog\/capacitor-everything-youve-ever-wanted-to-know\" target=\"_blank\" rel=\"noreferrer noopener\">everything you\u2019ve ever wanted to know about Capacitor.<\/a><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating a Capacitor App<\/strong><\/h3>\n\n\n\n<p>Since we are not using any framework in this tutorial, we can directly scaffold a basic Capacitor app like this:<\/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>Follow the prompts, give your app a name, and once you are done you can 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<p>We could already turn this into a native iOS and Android app now, but first, let&#8217;s add some more features.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Adding TailwindCSS<\/strong><\/h3>\n\n\n\n<p>You can style your web app as always with just CSS, but throwing in TailwindCSS makes it easy to create a great-looking app in minutes, 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  content: [&#039;.\/src\/**\/*.{html,js}&#039;],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};<\/code><\/pre>\n\n\n\n<p>Lastly, we need to load the Tailwind classes, so let&#8217;s 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:<\/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 the previous steps.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"743\" height=\"674\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image.png\" alt=\"\" class=\"wp-image-5138 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image.png 743w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-300x272.png 300w\" data-sizes=\"auto, (max-width: 743px) 100vw, 743px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 743px; --smush-placeholder-aspect-ratio: 743\/674;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"743\" height=\"674\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image.png\" alt=\"\" class=\"wp-image-5138\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image.png 743w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-300x272.png 300w\" sizes=\"auto, (max-width: 743px) 100vw, 743px\" \/><\/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\"><strong>Building a Capacitor App<\/strong><\/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. This might feel overwhelming, so we take a step back and delete the <code>src\/js\/capacitor-welcome.js<\/code> and also all references to it inside the src\/index.html. Instead, we will now build our own app from the ground up with basic Vanilla Javascript and then gradually integrate Capacitor functionality!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating a Navigation Bar<\/strong><\/h3>\n\n\n\n<p>Almost every app has a navigation bar, so we start by creating our own.<\/p>\n\n\n\n<p>Begin by adding a new file at <code>src\/js\/navbar.js<\/code>.<\/p>\n\n\n\n<p>The following code block now defines a custom navigation bar component in VanillaJS that can be reused across different pages (or even projects). This component has three slots, named &#8220;start,&#8221; &#8220;title,&#8221; and &#8220;end,&#8221; where you can insert your own elements like a button (following the idea of <a href=\"https:\/\/ionicframework.com\/docs\/components!\">Ionic components<\/a>).<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>window.customElements.define()<\/code> method is used to define the custom element with the name <code>custom-navbar<\/code> and a class that extends the <code>HTMLElement<\/code> class. The class constructor attaches a shadow DOM to the element, which means this component is separated from the rest of our DOM.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>root.innerHTML<\/code> property defines the HTML structure and CSS styles for our navigation bar component. The `:host` selector styles the host element, which is the custom element itself.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>.nav<\/code> class styles the navigation bar&#8217;s internal structure, which is a flexbox container with three child elements, each containing a slot element.<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code>::slotted()<\/code> pseudo-element styles the content inserted into the slots. In this case, the &#8220;title&#8221; slot is styled as an <code>h1<\/code> element, while the &#8220;start&#8221; and &#8220;end&#8221; slots are styled as div elements with white font color.<\/li>\n<\/ul>\n\n\n\n<p>Go ahead now and insert the following into the <code>src\/js\/navbar.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">\/\/ A simple reusable custom navigation ba element with 3 slots\nwindow.customElements.define(\n  &#039;custom-navbar&#039;,\n  class extends HTMLElement {\n    constructor() {\n      super();\n      const root = this.attachShadow({ mode: &#039;open&#039; });\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        justify-content: space-between;\n      }\n      ::slotted(h1) {\n        margin: 0;\n        font-size: 1em;\n        font-weight: 700;\n        color: #fff;\n      }\n      ::slotted(div) {\n        color: #fff;\n        margin: 0;\n      }\n    &lt;\/style&gt;\n    &lt;div class=&quot;nav&quot;&gt;\n      &lt;div&gt;&lt;slot name=&quot;start&quot;&gt;&lt;\/slot&gt;&lt;\/div&gt;\n      &lt;div&gt;&lt;slot name=&quot;title&quot;&gt;&lt;\/slot&gt;&lt;\/div&gt;\n      &lt;div&gt;&lt;slot name=&quot;end&quot;&gt;&lt;\/slot&gt;&lt;\/div&gt;\n    &lt;\/div&gt;\n    `;\n    }\n  }\n);<\/code><\/pre>\n\n\n\n<p>To include our new component, bring up the <code>index.html<\/code> and add the element and the import for our JavaScript file to the <code>body<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;body&gt;\n  &lt;!-- The custom nav bar, using 2 of the slots --&gt;\n  &lt;custom-navbar&gt;\n    &lt;h1 slot=&quot;title&quot;&gt;My Capacitor App&lt;\/h1&gt;\n    &lt;div slot=&quot;end&quot;&gt;\n      &lt;a href=&quot;\/info.html&quot;&gt;&lt;button&gt;Info&lt;\/button&gt;&lt;\/a&gt;\n    &lt;\/div&gt;\n  &lt;\/custom-navbar&gt;\n  &lt;script src=&quot;.\/js\/navbar.js&quot; type=&quot;module&quot;&gt;&lt;\/script&gt;\n&lt;\/body&gt;<\/code><\/pre>\n\n\n\n<p>At this point, you still don&#8217;t see the bar &#8211; because we used a CSS variable <code>--nav-bg<\/code> to style the background of our bar! This is how we can inject styling into our components, and how most <a href=\"https:\/\/ionicframework.com\/docs\/components\">Ionic Framework components<\/a> can be styled as well. We can simply style our bar by adding the variable to our <code>src\/css\/style.css<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">:root {\n  --nav-bg: theme(colors.blue.600);\n}<\/code><\/pre>\n\n\n\n<p>Rather than using a plain value, we access the TailwindCSS theme colors to achieve a consistent UI for our app.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7.png\" alt=\"\" class=\"wp-image-5169 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7-281x300.png 281w\" data-sizes=\"auto, (max-width: 686px) 100vw, 686px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 686px; --smush-placeholder-aspect-ratio: 686\/732;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7.png\" alt=\"\" class=\"wp-image-5169\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-7-281x300.png 281w\" sizes=\"auto, (max-width: 686px) 100vw, 686px\" \/><\/noscript><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Capturing images with the camera<\/strong><\/h3>\n\n\n\n<p>Let&#8217;s move on to building a more powerful component with <a href=\"https:\/\/capacitorjs.com\/docs\/plugins\">Capacitor plugins<\/a>!<\/p>\n\n\n\n<p>We want to capture images with the camera of a device, store them inside our app, and later display them in a list.<\/p>\n\n\n\n<p>To get started, let&#8217;s install these Capacitor plugins:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-shell\">npm install @capacitor\/camera\nnpm install @capacitor\/preferences\nnpm install @capacitor\/haptics<\/code><\/pre>\n\n\n\n<p>We are using the <a href=\"https:\/\/capacitorjs.com\/docs\/apis\/preferences\">Capacitor preferences<\/a> plugin to store images in this tutorial for simplicity &#8211; in a real-world scenario, you might want to either use the Filesystem directly or access the underlying SQLite database of a native app.<\/p>\n\n\n\n<p>To present our camera and a captured image, we need some new elements on our page, so bring up the src\/index.html and insert the new element into our body:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;div class=&quot;app-container&quot;&gt;\n  &lt;!-- The captured image --&gt;\n  &lt;img id=&quot;image&quot; class=&quot;p-4&quot; \/&gt;\n\n  &lt;!-- Box to upload an image --&gt;\n  &lt;div class=&quot;upload-box&quot; id=&quot;upload-box&quot;&gt;\n    &lt;div class=&quot;font-medium text-blue-600&quot; id=&quot;image-button&quot;&gt;\n      &lt;span&gt;Capture image&lt;\/span&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n\n  &lt;!--  Input fields --&gt;\n  &lt;div class=&quot;m-4&quot;&gt;\n    &lt;label \n        for=&quot;description&quot; \n        class=&quot;block text-sm font-medium leading-6 text-gray-900&quot;&gt;\n        Description\n    &lt;\/label&gt;\n    &lt;textarea\n      id=&quot;description&quot;\n      name=&quot;description&quot;\n      rows=&quot;3&quot;\n      class=&quot;description-input&quot;\n      placeholder=&quot;Describe your image&quot;&gt;\n    &lt;\/textarea&gt;\n    &lt;button class=&quot;button mt-4&quot; id=&quot;save&quot;&gt;Save Image&lt;\/button&gt;\n  &lt;\/div&gt;\n&lt;\/div&gt;\n&lt;script src=&quot;.\/js\/main.js&quot; type=&quot;module&quot;&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>For this example, I tried to hide most of the Tailwind-specific styling in standard classes, which means we need to add the following to our <code>src\/css\/style.css<\/code> now to correctly style our page:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">\/* Wrap our app content so it&#039;*s not covered by nav bar *\/\n.app-container {\n  margin-top: 60px;\n}\n\n\/* Style our main screen *\/\n.upload-box {\n  @apply cursor-pointer m-4 flex justify-center rounded-md border-2 border-dashed border-gray-300 px-6 pt-5 pb-6;\n}\n\n.description-input {\n  @apply w-full p-2 rounded-md text-gray-900 border-2 border-gray-300 placeholder:text-gray-400;\n}\n\n.button {\n  @apply w-full rounded bg-blue-600 py-3 px-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600;\n}<\/code><\/pre>\n\n\n\n<p>With that in place, we have a beautiful input dialog with a consistent color theme.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1.png\" alt=\"\" class=\"wp-image-5139 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1-281x300.png 281w\" data-sizes=\"auto, (max-width: 686px) 100vw, 686px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 686px; --smush-placeholder-aspect-ratio: 686\/732;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1.png\" alt=\"\" class=\"wp-image-5139\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-1-281x300.png 281w\" sizes=\"auto, (max-width: 686px) 100vw, 686px\" \/><\/noscript><\/figure>\n\n\n\n<p>However, there&#8217;s no functionality behind it (yet). For this, create a file at <code>src\/js\/main.js<\/code> and get started by importing our Capacitor plugins and adding an event listener to our <code>upload-box<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">import { Camera, CameraResultType, CameraSource } from &#039;@capacitor\/camera&#039;;\nimport { Preferences } from &#039;@capacitor\/preferences&#039;;\nimport { Haptics, ImpactStyle } from &#039;@capacitor\/haptics&#039;;\n\nexport const STORAGE_KEY = &#039;my-images&#039;;\n\n\/\/ Handle click to add image\ndocument.getElementById(&#039;upload-box&#039;).addEventListener(&#039;click&#039;, async (e) =&gt; {\n  \/\/ Capture image with Capacitor\n  const photo = await Camera.getPhoto({\n    resultType: CameraResultType.Base64,\n    source: CameraSource.Prompt,\n  });\n\n  \/\/ Show the selected image\n  const image = document.getElementById(&#039;image&#039;);\n  image.src = `data:image\/jpeg;base64,${photo.base64String}`;\n  image.style.display = &#039;block&#039;;\n\n  \/\/ Hide the dialog upload-box\n  document.getElementById(&#039;upload-box&#039;).style.display = &#039;none&#039;;\n});<\/code><\/pre>\n\n\n\n<p>In this function, we are now using the Capacitor camera package, which will open the native camera on a real device, and then add the resulting image as a base64 string to our image DOM element.<\/p>\n\n\n\n<p>For the browser, because there\u2019s no UI built-in to handle this, we can use the <a href=\"https:\/\/capacitorjs.com\/docs\/web\/pwa-elements\">PWA Elements<\/a> to handle this automatically for us.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-xml\">&lt;script\n  type=&quot;module&quot;\n  src=&quot;https:\/\/unpkg.com\/@ionic\/pwa-elements@latest\/dist\/ionicpwaelements\/ionicpwaelements.esm.js&quot;\n&gt;&lt;\/script&gt;\n&lt;script\n  nomodule\n  src=&quot;https:\/\/unpkg.com\/@ionic\/pwa-elements@latest\/dist\/ionicpwaelements\/ionicpwaelements.js&quot;\n&gt;&lt;\/script&gt;<\/code><\/pre>\n\n\n\n<p>This package gives a nice fallback UI on the web to capture an image!<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"738\" height=\"975\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3.png\" alt=\"\" class=\"wp-image-5141 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3.png 738w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3-227x300.png 227w\" data-sizes=\"auto, (max-width: 738px) 100vw, 738px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 738px; --smush-placeholder-aspect-ratio: 738\/975;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"738\" height=\"975\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3.png\" alt=\"\" class=\"wp-image-5141\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3.png 738w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-3-227x300.png 227w\" sizes=\"auto, (max-width: 738px) 100vw, 738px\" \/><\/noscript><\/figure>\n\n\n\n<p>You can even select an image file if you want because we set the source for the camera to <code>CameraSource.Prompt<\/code> which brings up this handy little sheet.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2.png\" alt=\"\" class=\"wp-image-5140 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2-281x300.png 281w\" data-sizes=\"auto, (max-width: 686px) 100vw, 686px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 686px; --smush-placeholder-aspect-ratio: 686\/732;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"732\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2.png\" alt=\"\" class=\"wp-image-5140\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2.png 686w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-2-281x300.png 281w\" sizes=\"auto, (max-width: 686px) 100vw, 686px\" \/><\/noscript><\/figure>\n\n\n\n<p>The selected image is displayed, so now we need a function to actually save it along with a little description.<\/p>\n\n\n\n<p>For this task, we add a new function that will grab the image and description and also load all information stored under our defined <code>STORAGE_KEY<\/code>.<\/p>\n\n\n\n<p>Capacitor Preferences use <code>UserDefaults<\/code> on iOS and <code>SharedPreferences<\/code> on Android, and falls back to local storage on the web.<\/p>\n\n\n\n<p>We add some logic to check if we already have stored images and then <code>push()<\/code> our new object, and before saving the data we need to make it a string as that&#8217;s the only type we can store with Capacitor preferences.<\/p>\n\n\n\n<p>Add the following to our <code>src\/js\/main.js<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-javascript\">\/\/ Handle click to safe image\ndocument.getElementById(&#039;save&#039;).addEventListener(&#039;click&#039;, async (e) =&gt; {\n  \/\/ Get the image data and input text\n  const image = document.getElementById(&#039;image&#039;).getAttribute(&#039;src&#039;);\n  const description = document.getElementById(&#039;description&#039;).value;\n\n  \/\/ Load any stored previous data\n  const { value } = await Preferences.get({ key: STORAGE_KEY });\n\n  \/\/ Add the new image\/description to the array\n  \/\/ or create a new array\n  \/\/ Then store the JSON.stringified() version to Preferences\n  if (value) {\n    const arr = JSON.parse(value);\n    arr.push({ image, description, });\n    await Preferences.set({ key: STORAGE_KEY, value: JSON.stringify(arr) });\n  } else {\n    const arr = [\n      {\n        image,\n        description,\n      },\n    ];\n    await Preferences.set({ key: STORAGE_KEY, value: JSON.stringify(arr) });\n  }\n\n  \/\/ Reset image and textarea\n  document.getElementById(&#039;image&#039;).style.display = &#039;none&#039;;\n  document.getElementById(&#039;description&#039;).value = &#039;&#039;;\n\n  \/\/ Show upload-box again\n  document.getElementById(&#039;upload-box&#039;).style.display = &#039;flex&#039;;\n\n  \/\/ Make our list reload with a custom event\n  const body = document.querySelector(&#039;body&#039;);\n  body.dispatchEvent(new CustomEvent(&#039;reload-list&#039;));\n\n  \/\/ Indicate list update with haptic feedback\n  await Haptics.impact({ style: ImpactStyle.Medium });\n});<\/code><\/pre>\n\n\n\n<p>Clicking the save button makes the image disappear now, but you can find the new entry in your browser debugging tools under storage (or application in Chrome):<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"859\" height=\"225\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4.png\" alt=\"\" class=\"wp-image-5142 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4.png 859w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4-300x79.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4-768x201.png 768w\" data-sizes=\"auto, (max-width: 859px) 100vw, 859px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 859px; --smush-placeholder-aspect-ratio: 859\/225;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"859\" height=\"225\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4.png\" alt=\"\" class=\"wp-image-5142\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4.png 859w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4-300x79.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-4-768x201.png 768w\" sizes=\"auto, (max-width: 859px) 100vw, 859px\" \/><\/noscript><\/figure>\n\n\n\n<p>That means we can use one API, but depending on the platform our code runs on the according implementation of that platform will be used!<\/p>\n\n\n\n<p><em>We have also used the <\/em><a href=\"https:\/\/capacitorjs.com\/docs\/apis\/haptics\"><em>Haptics plugin<\/em><\/a><em> which won&#8217;t work on the browser, but wait until we run this as a native app&#8230;<\/em><\/p>\n\n\n\n<p>There&#8217;s also a custom event reload-list we dispatch so we can reload our image list by listening to this &#8211; but of course, we first need that list.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Building a native iOS and Android App with Capacitor<\/strong><\/h2>\n\n\n\n<p>You can drop Capacitor into any web project no matter which framework you use, and simply add native iOS and Android folders from which you can deploy and release a real native mobile app &#8211; all in just minutes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Adding Native Platforms<\/strong><\/h3>\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-shell\">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>You can now directly deploy your app to a simulator by running:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npx cap run ios<\/code><\/pre>\n\n\n\n<p>You can select a simulator, and in a few seconds, you should see your native mobile app built with VanillaJS!<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"522\" height=\"1015\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5.png\" alt=\"\" class=\"wp-image-5145 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5.png 522w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5-154x300.png 154w\" data-sizes=\"auto, (max-width: 522px) 100vw, 522px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 522px; --smush-placeholder-aspect-ratio: 522\/1015;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"522\" height=\"1015\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5.png\" alt=\"\" class=\"wp-image-5145\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5.png 522w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-5-154x300.png 154w\" sizes=\"auto, (max-width: 522px) 100vw, 522px\" \/><\/noscript><\/figure>\n\n\n\n<p>At this point, we are leaving web land and diving into native app development, which you will notice because trying to capture an image will crash your app.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Adding native permissions<\/strong><\/h3>\n\n\n\n<p>For native apps, we need to ask for certain permissions, especially if we use plugins like the camera or file system. We can do this for iOS by opening the<code> ios\/App\/App\/Info.plist<\/code> and adding some elements to the <code>dict<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;key&gt;NSCameraUsageDescription&lt;\/key&gt;\n&lt;string&gt;To capture images&lt;\/string&gt;\n&lt;key&gt;NSPhotoLibraryAddUsageDescription&lt;\/key&gt;\n&lt;string&gt;To add images&lt;\/string&gt;\n&lt;key&gt;NSPhotoLibraryUsageDescription&lt;\/key&gt;\n&lt;string&gt;To select images&lt;\/string&gt;<\/code><\/pre>\n\n\n\n<p>The same needs to be done for Android, where we need to add the following at the bottom of the <code>android\/app\/src\/main\/AndroidManifest.xml<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; \/&gt;\n&lt;uses-permission android:name=&quot;android.permission.READ_EXTERNAL_STORAGE&quot; \/&gt;\n&lt;uses-permission android:name=&quot;android.permission.WRITE_EXTERNAL_STORAGE&quot; \/&gt;<\/code><\/pre>\n\n\n\n<p>This will make our app work, but we still have a somewhat ugly-looking UI issue at the top so let&#8217;s fix that as well.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Fixing the Native UI<\/strong><\/h3>\n\n\n\n<p>On native devices, we sometimes have a notch at the top or a safe area at the bottom &#8211; things we usually don&#8217;t have to worry about with a website.<\/p>\n\n\n\n<p>To fix these issues, let&#8217;s open the index.html and add a simple element:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;body&gt;\n  &lt;!-- Fix for iOS navbar gap --&gt;\n  &lt;div class=&quot;app-navbar-notch&quot;&gt;&lt;\/div&gt;\n  &lt;!-- Rest of your code --&gt;\n&lt;\/body&gt;<\/code><\/pre>\n\n\n\n<p>This element will be positioned at the top of our app and uses the <code>safe-area-inset-top<\/code> environment variable, which will be 0px inside a browser, but has a value inside our native app.<\/p>\n\n\n\n<p>Add the following to the <code>src\/css\/style.css<\/code>:<\/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>In previous code snippets I already made sure we respect that area within our content, so after running another build our top area should look much cleaner.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"522\" height=\"1015\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6.png\" alt=\"\" class=\"wp-image-5146 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6.png 522w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6-154x300.png 154w\" data-sizes=\"auto, (max-width: 522px) 100vw, 522px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 522px; --smush-placeholder-aspect-ratio: 522\/1015;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"522\" height=\"1015\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6.png\" alt=\"\" class=\"wp-image-5146\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6.png 522w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-6-154x300.png 154w\" sizes=\"auto, (max-width: 522px) 100vw, 522px\" \/><\/noscript><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Parting Thoughts<\/strong><\/h2>\n\n\n\n<p>We&#8217;ve seen that we can build powerful native mobile apps with Capacitor and VanillaJS without using any big Javascript framework.<\/p>\n\n\n\n<p>However, if you go down this route, I still recommend you <a href=\"https:\/\/ionicframework.com\/\">check out the Ionic Framework<\/a>, which is an open source mobile UI toolkit that gives you access to tons of components that adapt their look for iOS and Android. Ionic Framework also allows you to easily display a styled alert or modal to build truly amazing native mobile apps.<\/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\" target=\"_blank\" rel=\"noreferrer noopener\">iOS<\/a> and <a href=\"https:\/\/ionic.io\/blog\/building-and-releasing-your-capacitor-android-app\" target=\"_blank\" rel=\"noreferrer noopener\">Android<\/a> apps with the web.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>While Capacitor can be used with popular JavaScript frameworks like Angular and React, it is also possible to use it with VanillaJS. In this tutorial, we will learn how to use Capacitor with VanillaJS to create powerful native iOS and Android apps.<\/p>\n","protected":false},"author":11,"featured_media":5148,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,124],"tags":[151,25],"class_list":["post-5137","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all","category-tutorials","tag-capacitor","tag-tutorials"],"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>Create Powerful Native Mobile Apps with Capacitor &amp; VanillaJS - Ionic Blog<\/title>\n<meta name=\"description\" content=\"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.\" \/>\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\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Create Powerful Native Mobile Apps with Capacitor &amp; VanillaJS\" \/>\n<meta property=\"og:description\" content=\"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-04-27T14:58:28+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-04-27T14:58:30+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.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\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\"},\"author\":{\"name\":\"Simon Grimm\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6\"},\"headline\":\"Create Powerful Native Mobile Apps with Capacitor &#038; VanillaJS\",\"datePublished\":\"2023-04-27T14:58:28+00:00\",\"dateModified\":\"2023-04-27T14:58:30+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\"},\"wordCount\":1865,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png\",\"keywords\":[\"Capacitor\",\"Tutorials\"],\"articleSection\":[\"All\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\",\"url\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\",\"name\":\"Create Powerful Native Mobile Apps with Capacitor & VanillaJS - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png\",\"datePublished\":\"2023-04-27T14:58:28+00:00\",\"dateModified\":\"2023-04-27T14:58:30+00:00\",\"description\":\"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png\",\"width\":2240,\"height\":1120},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Create Powerful Native Mobile Apps with Capacitor &#038; 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":"Create Powerful Native Mobile Apps with Capacitor & VanillaJS - Ionic Blog","description":"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.","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\/create-powerful-native-mobile-apps-with-capacitor-vanillajs","og_locale":"en_US","og_type":"article","og_title":"Create Powerful Native Mobile Apps with Capacitor & VanillaJS","og_description":"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.","og_url":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs","og_site_name":"Ionic Blog","article_published_time":"2023-04-27T14:58:28+00:00","article_modified_time":"2023-04-27T14:58:30+00:00","og_image":[{"width":2240,"height":1120,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png","type":"image\/png"}],"author":"Simon Grimm","twitter_card":"summary_large_image","twitter_creator":"@schlimmson","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Simon Grimm","Est. reading time":"13 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs"},"author":{"name":"Simon Grimm","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/24d44b251756bd6488dcb741eec0bef6"},"headline":"Create Powerful Native Mobile Apps with Capacitor &#038; VanillaJS","datePublished":"2023-04-27T14:58:28+00:00","dateModified":"2023-04-27T14:58:30+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs"},"wordCount":1865,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png","keywords":["Capacitor","Tutorials"],"articleSection":["All","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs","url":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs","name":"Create Powerful Native Mobile Apps with Capacitor & VanillaJS - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png","datePublished":"2023-04-27T14:58:28+00:00","dateModified":"2023-04-27T14:58:30+00:00","description":"In this tutorial, we will learn how to build Capacitor apps with VanillaJS to create powerful native iOS and Android experiences.","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capvanilla-feature-image.png","width":2240,"height":1120},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/create-powerful-native-mobile-apps-with-capacitor-vanillajs#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Create Powerful Native Mobile Apps with Capacitor &#038; 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\/04\/capvanilla-feature-image.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5137","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=5137"}],"version-history":[{"count":12,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5137\/revisions"}],"predecessor-version":[{"id":5173,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5137\/revisions\/5173"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/5148"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=5137"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=5137"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=5137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}