> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solanamobile.com/llms.txt
> Use this file to discover all available pages before exploring further.

# solana-mobile webshell

> Wrap a web app in an Android WebView shell.

export const FooterDisclaimer = () => {
  return <p className="not-prose mt-16 text-center text-xs text-gray-500 dark:text-gray-400">
      Code samples on this page are subject to the{" "}
      <a className="underline underline-offset-2" href="https://www.apache.org/licenses/LICENSE-2.0">
        Apache 2.0 license
      </a>
      .
    </p>;
};

Turn an existing web app or PWA into an installable Android app: `init` generates an Android WebView project around your URL, and `build` produces a signed release APK you can publish on the [dApp Store](/dapp-store/submit-new-app).

<Note>
  The generated project is a WebView shell, not a [Trusted Web Activity
  (TWA)](/dapp-store/build-and-sign-an-apk) — it needs no Digital Asset Links
  setup. Links outside the configured host open in the system browser, and
  Solana wallet intents are handled natively by the shell.
</Note>

## webshell init

Generate the Android project. Run it without arguments to be prompted for everything, or pass the values you already know:

<CodeGroup>
  ```bash npm theme={null}
  npx solana-mobile@latest webshell init my-app --url https://example.com
  ```

  ```bash pnpm theme={null}
  pnpm dlx solana-mobile@latest webshell init my-app --url https://example.com
  ```

  ```bash bun theme={null}
  bunx solana-mobile@latest webshell init my-app --url https://example.com
  ```
</CodeGroup>

The directory defaults to the current one. `--force` overwrites an existing directory.

If your app already has a [web manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest), point `--manifest` at it — by path or URL — and the CLI seeds the app name, URL, and icons from it. A Bubblewrap `twa-manifest.json` works too, and also carries over the application id, versions, and signing key:

<CodeGroup>
  ```bash npm theme={null}
  npx solana-mobile@latest webshell init --manifest https://example.com/manifest.json
  ```

  ```bash pnpm theme={null}
  pnpm dlx solana-mobile@latest webshell init --manifest https://example.com/manifest.json
  ```

  ```bash bun theme={null}
  bunx solana-mobile@latest webshell init --manifest https://example.com/manifest.json
  ```
</CodeGroup>

The generated project keeps its configuration in `twa-manifest.json`, so `init` can be re-run against it later.

### Signing

`init` needs a signing keystore, and creates one when the path you give it does not exist yet. Set `SOLANA_MOBILE_KEYSTORE_PASSWORD` (and `SOLANA_MOBILE_KEY_PASSWORD`, if the key uses a different password) to skip the password prompt; otherwise you are asked for a password and a confirmation, since a typo produces a keystore you can never sign an update with. An existing keystore is reused as-is and needs no password until you build.

<Warning>
  Keep the keystore file and its passwords safe. Every future update of your app
  must be signed with the same key — losing it means you cannot update the app.
  See Google's [app signing
  guide](https://developer.android.com/studio/publish/app-signing#secure_key).
</Warning>

### Options

| Option                     | Description                                           |
| -------------------------- | ----------------------------------------------------- |
| `--app-name <name>`        | Application display name                              |
| `--application-id <id>`    | Android application id (e.g. `com.example.app`)       |
| `--force`                  | Overwrite an existing directory                       |
| `--keystore-alias <alias>` | Signing keystore alias (default `android`)            |
| `--keystore-path <path>`   | Signing keystore path (created when missing)          |
| `--manifest <path-or-url>` | Web `manifest.json` or Bubblewrap `twa-manifest.json` |
| `--url <url>`              | Web app URL to wrap                                   |
| `--version-code <number>`  | Android `versionCode`                                 |
| `--version-name <name>`    | Android `versionName`                                 |

## webshell build

Build a signed release APK from a webshell project:

<CodeGroup>
  ```bash npm theme={null}
  npx solana-mobile@latest webshell build my-app
  ```

  ```bash pnpm theme={null}
  pnpm dlx solana-mobile@latest webshell build my-app
  ```

  ```bash bun theme={null}
  bunx solana-mobile@latest webshell build my-app
  ```
</CodeGroup>

The directory defaults to the current one. The APK lands at `app/build/outputs/apk/release/app-release.apk`, ready to [install on a device](/cli/device#device-install) or submit to the dApp Store.

Signing passwords come from `SOLANA_MOBILE_KEYSTORE_PASSWORD` and `SOLANA_MOBILE_KEY_PASSWORD` so CI runs never prompt; when they are unset, the build asks for them.

### Options

| Option                     | Description                   |
| -------------------------- | ----------------------------- |
| `--keystore-alias <alias>` | Signing keystore alias        |
| `--keystore-path <path>`   | Signing keystore path         |
| `--stacktrace`             | Pass `--stacktrace` to Gradle |

## Environment variables

| Variable                          | Description                                           |
| --------------------------------- | ----------------------------------------------------- |
| `SOLANA_MOBILE_KEY_PASSWORD`      | Signing key password — defaults to the store password |
| `SOLANA_MOBILE_KEYSTORE_PASSWORD` | Keystore password                                     |

<FooterDisclaimer />
