Skip to content

Nuxt + Ionic Local Development

nuxt-ionic-local-development

Nuxt is a powerful framework based on Vue, with file-based routing, code splitting, auto-imports, zero-config TypeScript support, etc., while [Ionic](https:// ionicframework.com/) allows us to use Web to build modern, high quality cross-platform mobile apps. Is it possible to combine the two for development?

Fortunately, Nuxt provides the @nuxt/ionic module to integrate with Ionic. Unfortunately, as of v0.13.1, Nuxt + Ionic local development is still a challenge, and if you follow the documentation, you're likely to run into bugs and not be able to move forward. The purpose of this article is to document the process of reproducing local development errors and exploring possible solutions.

Replicating Local Development Errors

Let's start by trying to develop locally according to the documentation. My development environment is macos Sonoma 14.4.1, Node.js v20.11.0 and PNPM v8.15.5.

Refer to Nuxt Docs > Installation to create a new Nuxt project.

shell
pnpm dlx nuxi@latest init nuxt-ionic

Set shamefully-hoist to true in .npmrc.

shell
shamefully-hoist=true

Refer to @nuxt/ionic Docs > Installation to install @nuxt/ionic, Android Studio, etc. Refer to React Native > Build Development Environment to configure the environment and create an emulator.

shell
pnpm install --force && pnpm install @nuxtjs/ionic -D

Update nuxt.config.ts.

typescript
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
  devtools: { enabled: true },
  modules: ['@nuxtjs/ionic'], 
  ssr: false, 
});

Install the dependencies again and trigger the postinstall hook, which lets Nuxt create the Ionic-related files.

shell
pnpm install

Refer to @nuxt/ionic Docs > Enabling Capacitor to enable Capacitor.

shell
pnpm add -g @ionic/cli
ionic config set -g npmClient pnpm
ionic integrations enable capacitor
ionic capacitor add ios
ionic capacitor add android

Refer to @nuxt/ionic Docs > Routing to update app.vue.

vue
<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Home</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content class="ion-padding">Hello World</ion-content>
  </ion-page>
</template>

At this point, the project has been initialized, you can start the project with pnpm run dev -o and you will see the following page in your browser.

nuxt-ionic-local-development-1

Developing a mobile application is not just about running the project on a browser, we also need to run the project on an emulator or a real machine, as demonstrated here using Android. Referring to @nuxt/ionic Docs > Local Development, create . /scripts/android.sh and write the following.

shell
#!/bin/bash
LIP=$(ipconfig getifaddr en0)

echo "🍦 Starting local development to android device - ensure local dev server is running already"
echo "🏗️ Type checking and building for development..."
pnpm run build:dev
echo "🔃 Capacitor installation, podfile installation, sync and copy to app distribution folders..."
npx @ionic/cli capacitor sync android --no-build
echo "🏃 Select an Android device to run the build at local ip address ${LIP} on..."
eval "npx @ionic/cli capacitor run android --livereload-url=http://${LIP}:3000 --external --mode development"

Update the scripts field in package.json.

json
{
  "scripts": {
    "android": "bash ./scripts/android.sh"
  }
}

🥰 Let's start the project with anticipation!

shell
pnpm run android

😱 The result is a vicious error!

nuxt-ionic-local-development-2

If you are using Windows, it is recommended to use the Git Bash that came with your Git installation. ipconfig getifaddr en0 command may report an error, and the error message may not match the legend I provided, so you can skip it and go straight to the next section, which removes the command later.

Explore possible solutions

Let's take a closer look at the errors. The first error is Missing script: build:dev, which means that the command build:dev does not exist. The second and third errors are Could not find the web assets directory: . /dist, which means that the dist directory does not exist.

If you're familiar with Nuxt, then I'm sure the answer to this is to generate the dist directory via pnpm run generate, so that the rest of the process can run correctly. If you're not familiar with it, what should you do? The easiest and most efficient way is to go through the @nuxt/ionic and Ionic documentation again. In @nuxt/ionic Docs > Enabling Capacitor, the steps are outlined.

nuxt-ionic-local-development-3

  1. The error message indicates that we need to generate a dist directory. The @nuxt/ionic documentation says we can use nuxi generate or nuxi build to generate it, but in fact only nuxi generate generates the dist directory, so we need to use nuxi generate, which means running pnpm run generate. So we need to use nuxi generate, which means running pnpm run generate.
  2. we need to run npx cap sync (the npx @ionic/cli capacitor sync used earlier) to synchronize the dist directory.
  3. We need to run npx cap open ${platform} (also known as @ionic/cli capacitor run) to open the app for the corresponding platform (Android / iOS).

Let's replace pnpm run build:dev with pnpm run generate and try starting it again.

shell
#!/bin/bash
LIP=$(ipconfig getifaddr en0)

echo "🍦 Starting local development to android device - ensure local dev server is running already"
echo "🏗️ Type checking and building for development..."
pnpm run build:dev
pnpm run generate
echo "🔃 Capacitor installation, podfile installation, sync and copy to app distribution folders..."
npx @ionic/cli capacitor sync android --no-build
echo "🏃 Select an Android device to run the build at local ip address ${LIP} on..."
eval "npx @ionic/cli capacitor run android --livereload-url=http://${LIP}:3000 --external --mode development"

This time it doesn't report an error in the terminal, but it does in the Android emulator.

nuxt-ionic-local-development-4

It says here that it can't connect to http://192.168.0.103:3000, but in reality we don't need to connect here at all, we can remove the relevant part and try to start it again.

shell
#!/bin/bash
LIP=$(ipconfig getifaddr en0)

echo "🍦 Starting local development to android device - ensure local dev server is running already"
echo "🏗️ Type checking and building for development..."
pnpm run generate
echo "🔃 Capacitor installation, podfile installation, sync and copy to app distribution folders..."
npx @ionic/cli capacitor sync android --no-build
echo "🏃 Select an Android device to run the build at local ip address ${LIP} on..."
eval "npx @ionic/cli capacitor run android --livereload-url=http://${LIP}:3000 --external --mode development"
echo "🏃 Select an Android device to run the build at local on..."
npx @ionic/cli capacitor run android --external

This step also removes the invocation of the ipconfig getifaddr en0 command, and the results of the following steps should be consistent across systems.

This time a new prompt appears in the terminal. It wants to run vue-cli-service build but doesn't find @vue/cli-service and asks if we want to install it.

nuxt-ionic-local-development-5

But we already have the dist directory, so why build it? Let's add the --no-build parameter to skip this step and try starting again.

shell
#!/bin/bash
echo "🍦 Starting local development to android device - ensure local dev server is running already"
echo "🏗️ Type checking and building for development..."
pnpm run generate
echo "🔃 Capacitor installation, podfile installation, sync and copy to app distribution folders..."
npx @ionic/cli capacitor sync android --no-build
echo "🏃 Select an Android device to run the build at local on..."
npx @ionic/cli capacitor run android --external
npx @ionic/cli capacitor run android --external --no-build

🎉 This time we found the Android simulator working properly!

nuxt-ionic-local-development-6

😔 Unfortunately, the terminal just exits, which means that after we change the code, we still need to manually run pnpm run android in order to see the latest code results. Is there any way to run it automatically after changing the code? 🤔

When you don't have a clue, asking AI is a good option. I'm using the VSC free plugin Codeium here.

nuxt-ionic-local-development-7

We can check them one by one, and the first one nodemon turns out to be very good, so let's use it. In fact, nodemon is one of the most widely used packages for our needs.

shell
pnpm install nodemon -D

There are a lot of directories and file types we need to listen to, and the scripts field in package.json would be very long, so we'll use the configuration file nodemon.json to set up nodemon.

json
{
  "ignore": [".git", "**/node_modules/"],
  "watch": [
    "./assets",
    "./components",
    "./composables",
    "./content",
    "./layouts",
    "./middleware",
    "./modules",
    "./pages",
    "./plugins",
    "./public",
    "./server",
    "./utils",
    "./.env",
    "./app.vue",
    "./app.config.ts",
    "./capacitor.config.json",
    "./error.vue",
    "./ionic.config.json",
    "./nuxt.config.ts",
    "./tsconfig.json"
  ],
  "ext": "json,js,jsx,ts,tsx,vue"
}

Finally, update the scripts field in package.json.

json
{
  "scripts": {
    "android": "nodemon --exec \"bash ./scripts/android.sh\""
  }
}

Running pnpm run android again to check it results in another hard error! 😡

nuxt-ionic-local-development-8

This is because when we use nodemon, we can't interact with the terminal and the command npx @ionic/cli capacitor run android --external --no-build doesn't specify a target, which results in an error.

We can fix this by running ionic capacitor run android --list to get the optional devices and automatically pass the first device ID.

shell
#!/bin/bash
target=$(ionic capacitor run android --list | awk 'NR>3 {print $NF; exit}')

echo "🍦 Starting local development to android device - ensure local dev server is running already"
echo "🏗️ Type checking and building for development..."
pnpm run generate
echo "🔃 Capacitor installation, podfile installation, sync and copy to app distribution folders..."
npx @ionic/cli capacitor sync android --no-build
echo "🏃 Select an Android device to run the build at local on..."
npx @ionic/cli capacitor run android --external --no-build
eval "npx @ionic/cli capacitor run android --external --no-build --target=${target}"

If you are using Windows, you need to install the awk command first, e.g. using chocolatey: choco install awk.

Then run pnpm run android again to check that everything is working now, and you can see the latest code effect after modifying the code! 🎉

The script provided above can be adapted to iOS with some simple modifications, so I won't repeat it here. The final package.json is as follows.

json
{
  "name": "nuxt-app",
  "private": true,
  "type": "module",
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare",
    "android": "nodemon --exec \"bash ./scripts/android.sh\""
  },
  "dependencies": {
    "@capacitor/android": "5.7.4",
    "@capacitor/app": "5.0.7",
    "@capacitor/core": "5.7.4",
    "@capacitor/haptics": "5.0.7",
    "@capacitor/ios": "5.7.4",
    "@capacitor/keyboard": "5.0.8",
    "@capacitor/status-bar": "5.0.7",
    "nuxt": "^3.11.1",
    "vue": "^3.4.21",
    "vue-router": "^4.3.0"
  },
  "devDependencies": {
    "@capacitor/cli": "5.7.4",
    "@nuxtjs/ionic": "^0.13.1",
    "nodemon": "^3.1.0"
  }
}

Summary

This article documents the process of combining Nuxt and Ionic for local development, including configuring the environment, creating a project, encountering problems, and exploring solutions.

There may be some deficiencies in the documentation of the combination of Nuxt and Ionic, but we can improve this part of the use of Nuxt by reading and understanding the error messages, and combining the official documentation, community resources, and AI assistance.

I hope this article has inspired and helped you!

Released under the MIT License.