6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsでネイティブ機能付きのモバイルアプリを開発してみる [capacitor.js]

6
Last updated at Posted at 2025-12-22

※ こちら、あくまで実験的に動かせたレベルの話ですので、本番での利用を保証するものではありません。ご了承ください。

Nextjsで、iOS、Androidのアプリを開発する方法を紹介します。
ウェブビューを表示するだけでなく、カメラの起動や通知などモバイル専用の機能を利用してアプリを構築できます。

Server Actions等のモダンなウェブ技術を活用してモバイルアプリを開発できるようになります。

Nextjsでモバイルアプリを作れる仕組み

capacitor.jsというライブラリを用いて、Nextjsをネイティブアプリに変換します。

ざっくりとした仕組みは、モバイルアプリの中でWebViewを表示して、WebViewからネイティブ機能にアクセスできるようにするというものです。
スクリーンショット 2025-12-23 1.07.27.png

ハンズオン

今回はネイティブのGPS機能を用いて、ウォーキングを記録するミニアプリを作ります。
こんな感じのシンプルなアプリです。
Simulator Screenshot - iPhone 16 Pro - 2025-12-23 at 00.35.10.png

ボタンを押したら位置情報を取得できる単純なものですが、これを発展させると、ランニングの記録アプリなども構築できるかと思います。

セットアップ

nextjsのプロジェクトを作成します。(既存のものでもOK)

npx create-next-app@latest

capacitorを入れます。2025/12現在、v8が最新版ですが、私の環境が悪いのかうまく動作しなかったので、v7を指定します。

npm i "@capacitor/core@^7"
npm i -D "@capacitor/cli@^7"

以下を実行。設定はそのままでOKかと。

npx cap init

生成されたcapacitor.config.tsを以下のように変更します。
serverのurlがキモです。webビューのurlを指定しています。nextjsの開発環境かデプロイのurlを指定してください。・

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'capacitor-test', // 好きな名前に変更
  webDir: 'dist', // これはなんでもOK。使われません。
  server: {
    url: "http://localhost:3000" // もしくはnextjsのデプロイURLに変更
  }
};

export default config;

iOS、Android用のパッケージを入れます。(今回のデモではiOSだけ使います)

pnpm add @capacitor/android @capacitor/ios

このコマンドでルートディレクトリ直下にiosディレクトリを生やし、iOSアプリを生成します。

npx cap add ios

xcodeで開きます。

npx cap open ios

ビルドしようとすると怒られるので、Termを変えます。スクショでNoneになってる部分を変更してください。
スクリーンショット 2025-12-22 16.24.44.png

再生ボタンを押してシミュレータを起動すると、Nextjsの初期画面が描画されます。
8dab6275-8ee2-46c2-a780-961c54ae32a2.png

無事にNextjsでアプリを作れましたね。
これでセットアップが完了しました。
現状ですとまだ単純なwebビューの表示に過ぎず、PWAと変わりありません。
続いて、GPSやバックグラウンド処理など、ネイティブな機能をつけて、ネイティブアプリらしくしていきます。

NextJSでネイティブ機能を利用

続いて、Nextjsのコンポーネントからネイティブ機能にアクセスする方法を説明します。

Capacitorを使えば、javascriptからモバイルのネイティブ機能にアクセスできます。
カメラ、位置情報、音声等の各種機能がプラグインとして配布されており、importして使えます。
今回は、@capacitor/geolocationを利用して、GPS機能を使って座標を取得してみます。

まずはこちらを読んで導入します。
https://capacitorjs.com/docs/v7/apis/geolocation
以下を実行します。

npm install @capacitor/geolocation@latest-7
npx cap sync

次に、info.plistに以下の権限を追加します。

NSLocationAlwaysAndWhenInUseUsageDescription (Privacy - Location Always and When In Use Usage Description)
NSLocationWhenInUseUsageDescription (Privacy - Location When In Use Usage Description)

これで準備完了です。
ここまでくると、クライアントコンポーネント上で、Geolocationの各種機能をつかってGPS情報(緯度経度等)を取得できます。

import { Geolocation } from "@capacitor/geolocation";
// ...
const updateCoords = async() => {
    // 位置情報の利用を許可するかユーザーに聞くモーダルを表示
    await Geolocation.requestPermissions()
    // 緯度経度の取得
    const position = await Geolocation.getCurrentPosition();
    const { latitude, longitude } = position.coords;
    setCoords({latitude, longitude})
}

上記コードのawait Geolocation.requestPermissions()は、「あなたの位置情報を利用してもいいですか?」というダイアログを出す関数です。許可された時のみに次のステップの処理が成功します。
スクリーンショット 2025-12-23 0.37.06.png

ボタンを押すと緯度経度を拾って描画するコンポーネントを作ってみました。

"use client"
import { Geolocation } from "@capacitor/geolocation";
import { useState } from "react";

const LocationViewer = () => {
    const [coords, setCoords] = useState<{latitude:number, longitude: number}>()
    const updateCoords = async() => {
        await Geolocation.requestPermissions()
        const position = await Geolocation.getCurrentPosition();
        const { latitude, longitude } = position.coords;
        setCoords({latitude, longitude})
    }
    return <div className="min-h-screen bg-slate-950 px-6 py-24 text-slate-100">
        <div className="mx-auto w-full max-w-md rounded-2xl border border-slate-800 bg-slate-900/60 p-6 shadow-[0_20px_60px_-40px_rgba(15,23,42,0.9)]">
            <h1 className="text-lg font-semibold tracking-wide text-slate-100">位置情報</h1>
            <p className="mt-1 text-sm text-slate-400">現在地の緯度・経度を取得します。</p>
            <button
                onClick={updateCoords}
                className="mt-6 w-full rounded-xl bg-emerald-500 px-4 py-3 text-sm font-semibold text-emerald-950 shadow-[0_10px_30px_-20px_rgba(16,185,129,0.9)] transition hover:bg-emerald-400 active:scale-[0.99]"
            >
                位置情報取得
            </button>
            <div className="mt-6 rounded-xl border border-slate-800 bg-slate-950/60 px-4 py-3 text-sm text-slate-200">
                {coords ? (
                    <div className="flex flex-col gap-1">
                        <span>緯度: {coords.latitude}</span>![simulator_screenshot_3CA0217D-3012-4322-A5DE-8C1DCD507E0D.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2984288/39f0622a-dd27-46f6-ad90-8397123f449b.png)

                        <span>経度: {coords.longitude}</span>
                    </div>
                ) : (
                    <span className="text-slate-500">未取得</span>
                )}
            </div>
        </div>
    </div>
}

export default LocationViewer

アプリで表示した時の画面です。

  1. 初期画面
    b6993a6a-15f8-413b-8eb2-29145c0bd132.png

  2. 位置情報の利用許諾
    315361da-4f90-4ff9-b4a7-378b81d1ed53.png

  3. 位置情報の表示
    Simulator Screenshot - iPhone 16 Pro - 2025-12-23 at 00.35.10.png

Server Actionsと併用しても問題なし

試しに処理の末尾にServer Actionsをつけてみましたが問題はありませんでした。

"use server"

export async function testAction() {
    return "test"
}

"use client"
// ...
const updateCoords = async() => {
    await Geolocation.requestPermissions()
    const position = await Geolocation.getCurrentPosition();
    const { latitude, longitude } = position.coords;
    setCoords({ latitude, longitude })
    const actionResult = await testAction();
    console.log(actionResult) // xcodeのコンソールでtestと表示されました。
}

ということは、DBなどの外部サービスと連携できますね。

最後に・注意点

Nextjsでネイティブアプリを開発する方法を書きました。
今回、iOSの場合を取り上げましたが、Androidの場合もほぼ同じやり方でできます。

今回のチュートリアルではボタンを押したら位置情報を取得できる単純なものでしたが、バックグラウンドでの位置情報の取り込みも可能です。ですので、ランニングの記録アプリなどもNext.jsで構築できるかと思います。

私もこの技術の検討を進めていきます。
正直、プロダクションでどれだけ事故なく使えるかまでは知見がないので、試された方がいたら共有していただけると幸いです。

最後まで読んでいただきありがとうございます。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?