Getting started with Capacitor using Next.js on Android

Ever wondered how to run a Next.js app natively on a mobile device? It requires some setup but it's actually very easy thanks to Ionic's Capacitor! We also have a Capacitor + Next.js + iOS guide!

What is Capacitor?

Capacitor is Ionic's solution to running web apps natively on your device. To put it simply, if your app works on the web, Capacitor is an easy solution to use your already existing front-end while still being able to access your phone's functionality.

It's an amazing solution and it will work out of the box with most front-end frameworks such as React, Vue and Angular.

So, is Next.js compatible with Capacitor?

Yes, sort-of... after some fiddling. Don't get me wrong here, Next.js is not specifically built for this since it has a strong emphasis on Server Side Rendering (to appease search engines like Google).

However, Next.js without SSR is still a very useful framework that simplifies vanilla React a whole lot thanks to some extra functionality. This is definitely enough of a reason to decide using it for your next non-SSR project.

How to use Next.js and Capacitor together on Android

First of all, I suggest you follow Capacitor's Quick Start guide if you haven't done it already. That's really all you need before we dive into fixing the initial problems.

Remove server-side functions from your app

If your app is already fully built and is using Next-specific functionality such as getServerSideProps or getInitialProps I have some bad news: these won't work with Capacitor.

At this point you need to make a fairly important decision. Do you:

  • rework your app so that you don't need them
  • create a slightly different codebase to work with Capacitor

Unfortunately these are the only solutions as our final goal is to statically export our app into HTML and Javascript files.

Thankfully getStaticProps is still very much usable, as it is called during the export to compile static pages.

Fixing Unable to open asset URL error in Android Studio

This one confused me for a while before I was able to fix it. If you've already tried opening Android Studio to test your app on Android, you might notice how your app is technically working, but is unfortunately missing all its styles and Javascript.

A quick look at Android Studio's console will show a fairly long list of Unable to open asset URL errors.

This is actually caused by the way Android Studio is built to work, as it turns out it will automatically ignore any folder prefixed with _, which unfortunately means our _next folder (which contains all of our assets) is not being imported by Android!

To fix this we just need to change our Android project setup. In Android studio, open up the file marked as build.gradle (Module: app) which should be contained inside Gradle Scripts. It should look something like this:

apply plugin: 'com.android.application'

android {
    defaultConfig {
        // This should contain a bunch of configurations
    }
}

All we need to do then is add an aaptOptions parameter to this object so that it looks like this:

apply plugin: 'com.android.application'

android {
    defaultConfig {
        aaptOptions {
            ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~'
        }
    }
}

Keep everything else in the file the same!

Once this is done Android Studio should prompt you to Sync your Gradle Files. Let it do its thing and when try to open your app once more, it should work.

Setting up live reloading in Capacitor

Capacitor, as well as your app, should now be working. However you might notice that developing your app live requires you to automatically build and reload files.

This obviously isn't ideal and would take quite a lot of time to make any meaningful progress so let's set up live reload so you can see the changes.

First of all open your capacitor.config.json. You should already have one and it should contain generic information about your app such as name and ID. In this config you'll want to add a server object like this:

{
  // Other configs
  "server": {
    "url": "http://<your computer's ip>:<the port your app runs on>"
  }
}

This will tell capacitor not to run its own server when running the app, but rather get the app from an external source. Yes, this does technically mean you could get your app from a live URL.

I actually haven't tested getting the app content from a URL, but expect a future article explaining how well that works!

Make sure you sync your project with Android studio with:

npx cap sync android

And then open Android Studio with:

npx cap open android

The last thing you'll want to do here is open /capacitor-android/manifests/AndroidManifest.xml and make sure your XML config contains the following:

<application android:usesCleartextTraffic="true">

This will ensure Capacitor can connect to your development server without HTTPS.

All that's left for you to do is start your app in development mode:

npm run dev

And after starting your app in Android Studio, the app should automatically open on your device. Feel free to edit any of your files - you should immediately see the changes reflected on your device.

Remember to remove the whole server object from your config once you decide to publish your app or Capacitor will keep trying to load an empty address!

Final thoughts on Capacitor

I'm personally really keen to get more experience with Capacitor over the next couple of months.

I'm especially excited about Capacitor Elements, which are 1:1 replicas of native elements from both iOS and Android to make your app feel more native. There's still no word on when they'll be out, but I'm looking forward to Capacitor's future.

Stay tuned for more tutorials and Capacitor info.

Also, make sure you also check out our how to get started with Capacitor and Next.js on iOS if you haven't already!