Getting Started

Install Vue Solana packages, configure Vue or Nuxt, and test RPC reads on devnet.

This guide covers installing the Vue Solana packages, configuring Vue or Nuxt, testing Solana RPC reads, connecting supported wallets, signing messages, sending a real devnet transfer, and verifying the result. The examples use devnet by default for safe testing.

Before You Start

Use @vue-solana/core directly if you need Solana primitives without Vue/Nuxt integration. It builds on @solana/kit and re-exports createSolanaClient() plus all of @solana/kit from @vue-solana/core/kit. Use @vue-solana/vue or @vue-solana/nuxt when you want framework integration.

Supported clusters:

  • mainnet: Solana's production cluster. This is Solana's official mainnet cluster name.
  • devnet: best default for app development.
  • testnet: validator and protocol testing network.
  • localnet: local validator.

Use devnet while learning and testing. Use mainnet only when you are ready to interact with real SOL.

Current wallet support:

  • Browser extension wallets through Solana Wallet Standard packages.
  • Android native mobile wallets through @solana-mobile/wallet-standard-mobile on Android Chrome and Chrome PWAs.
  • iOS browser wallets for Phantom, Solflare, and Backpack through wallet-specific universal links.
  • Manual/custom wallet objects that implement SolanaWallet.

Planned but not supported yet:

  • Desktop native app wallets through wallet-specific protocol links or future native Wallet Standard registration.

Install For Vue

pnpm add @vue-solana/vue
npm install @vue-solana/vue

Vue apps can use @vue-solana/vue/kit (createSolanaClient, address, lamports, and types) and @vue-solana/vue/buffer-polyfill without installing low-level Solana or Buffer packages directly. Use useSolanaClient() from @vue-solana/vue/useSolanaClient for the injected client.

Install For Nuxt

npx nuxt module add @vue-solana/nuxt

This installs the package and adds @vue-solana/nuxt to the modules array in nuxt.config.ts.

Nuxt apps can use @vue-solana/nuxt/kit and @vue-solana/nuxt/buffer-polyfill without installing @vue-solana/core, @vue-solana/vue, or low-level Solana and Buffer packages directly. The auto-imported useSolanaClient() returns the injected Kit client.

v3 Note

v3.0.0 publishes ESM only — the require export condition and the top-level main field are gone from every @vue-solana/* package. Nuxt and Vite apps already bundle ESM and need no change; a plain Node script that require()s a Vue Solana package must become ESM. The kit subpaths also became a complete mirror of @solana/kit, so you can drop @solana/kit from your own package.json if you added it on v2. See Upgrading v2 to v3 for the details.

v2 Note

v2.0.0 removed the legacy @solana/web3-compat surface. The context no longer carries a connection, and the @vue-solana/*/web3 subpaths were deleted. All composables are Kit-first and SolanaWallet.publicKey is a plain base58 Address string. The @solana/buffer/ shim that earlier v1 docs described is gone; the retained package-owned shims only cover the browser buffer/ subpath used by the Buffer polyfill. See the Kit Migration guide for the full before/after map.

Vue Setup

import { createApp } from "vue";
import { createSolanaPlugin } from "@vue-solana/vue";
import App from "./App.vue";

createApp(App)
  .use(
    createSolanaPlugin({
      cluster: "devnet",
      mobileWallet: {
        appIdentity: {
          name: "My Vue Solana App",
        },
      },
      iosWallet: {
        appIdentity: {
          name: "My Vue Solana App",
        },
      },
    }),
  )
  .mount("#app");

mobileWallet and iosWallet are optional. Android Mobile Wallet Adapter registration and iOS Phantom, Solflare, and Backpack links are enabled by default when the browser runtime supports them. Pass mobileWallet: false or iosWallet: false to disable either source.

For Vue composables, prefer direct subpath imports in new code:

import { useRpc } from "@vue-solana/vue/useRpc";
import { useBalance } from "@vue-solana/vue/useBalance";
import { useSolanaClient } from "@vue-solana/vue/useSolanaClient";

useRpc() returns the resolved cluster state and the injected Kit client; useBalance() reads through client.rpc. useSolanaClient() returns the same client and its read-only rpc directly, plus address()/lamports() from @vue-solana/vue/kit. See the Kit Migration guide for the full before/after map.

Client and Plugin Lifecycle

createSolanaPlugin() builds the Kit client once, during install(). Create the plugin at module scope and reuse the instance:

// solana.ts
import { createSolanaPlugin } from "@vue-solana/vue";

export const solana = createSolanaPlugin({ cluster: "devnet" });

Calling createSolanaPlugin() again builds a new client and context, discarding the existing wallet selection and RPC state. If your config is reactive — a cluster toggle, for example — memoize on the config so a new plugin (and client) is built only when the value actually changes, not on every render:

import { computed, ref } from "vue";

const cluster = ref<SolanaCluster>("devnet");
const plugin = computed(() => createSolanaPlugin({ cluster: cluster.value }));

A Kit client runs its createClient().use(...) plugins during construction. When one of those plugins is async, the client — and any context built from it — only activates after that promise resolves. Defer real RPC and wallet work to lifecycle hooks or user actions after hydration rather than running it during setup or SSR.

Nuxt Setup

export default defineNuxtConfig({
  modules: ["@vue-solana/nuxt"],
  solana: {
    cluster: "devnet",
    mobileWallet: {
      appIdentity: {
        name: "My Nuxt Solana App",
      },
    },
    iosWallet: {
      appIdentity: {
        name: "My Nuxt Solana App",
      },
    },
  },
});

The Nuxt module installs the runtime plugin on the client only and auto-imports composables from direct @vue-solana/vue/* subpaths. Composables are safe to call during SSR, but real RPC and wallet operations should run after hydration, such as from onMounted() or user actions. Nuxt solana options live in public runtime config, so keep them JSON-serializable.

Direct Vue and core clients accept payer and payerSecretKey for client-sent transactions. payerSecretKey is a base64 64-byte Ed25519 keypair, so never put it in Nuxt public runtime config or ship a funded key to a browser. Nuxt ModuleOptions intentionally omits both fields, so install a client-owned payer in a client-only plugin with clientPlugin: false so the module does not install a second plugin.

Test RPC Without A Wallet

RPC reads work without a browser wallet.

In Vue, use useRpc():

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useRpc } from "@vue-solana/vue/useRpc";

const { cluster, endpoint, client } = useRpc();
const latestBlockhash = ref<string | null>(null);

onMounted(async () => {
  const { value } = await client.rpc.getLatestBlockhash().send();
  latestBlockhash.value = value.blockhash;
});
</script>

<template>
  <main>
    <p>Cluster: {{ cluster }}</p>
    <p>Endpoint: {{ endpoint }}</p>
    <p>Latest blockhash: {{ latestBlockhash }}</p>
  </main>
</template>

New code can use the Kit client instead. useSolanaClient() needs no wallet and returns the same info typed for the Kit RPC API (read calls return bigint):

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { useSolanaClient } from "@vue-solana/vue/useSolanaClient";

const { rpc } = useSolanaClient();
const slot = ref<bigint | null>(null);

onMounted(async () => {
  slot.value = await rpc.getSlot().send();
});
</script>

<template>
  <main>
    <p>Slot: {{ slot }}</p>
  </main>
</template>

In Nuxt, use the auto-imported useSolanaRpc():

<script setup lang="ts">
const { cluster, endpoint, checkConnection, latestBlockhash } = useSolanaRpc();
</script>

<template>
  <main>
    <p>Cluster: {{ cluster }}</p>
    <p>Endpoint: {{ endpoint }}</p>
    <p>Latest blockhash: {{ latestBlockhash }}</p>
    <button type="button" @click="checkConnection">Check RPC</button>
  </main>
</template>

In Nuxt, the same Kit reads come from the auto-imported useSolanaClient():

<script setup lang="ts">
const { rpc } = useSolanaClient();
const slot = ref<bigint | null>(null);

onMounted(async () => {
  slot.value = await rpc.getSlot().send();
});
</script>

Get Devnet Or Testnet SOL

Devnet and testnet SOL are testing tokens with no real value.

Use the official faucet:

https://faucet.solana.com

Choose Devnet while following this guide. Choose Testnet only if you are testing against the testnet cluster.

If you have the Solana CLI installed, you can also run:

solana airdrop 1 YOUR_WALLET_ADDRESS --url devnet
solana airdrop 1 YOUR_WALLET_ADDRESS --url testnet

Never use a wallet with real funds while testing.

Run The Examples

To run the example apps locally, clone the Vue Solana repository first:

git clone https://github.com/vue-solana/vue-solana.git
cd vue-solana
pnpm install
pnpm build:packages

Start the Vue Vite example:

pnpm dev:vue

Start the Nuxt example:

pnpm dev:nuxt

The examples demonstrate plugin/module setup, RPC state, direct connection calls, balance reads, unified wallet discovery, persisted wallet selection, wallet state, message signing, generic transaction state, transaction transfer flows, client-sent transactions through the official Kit planner and RPC plan-sending executor, confirmation status, explorer links, and unsupported capability UI. They use devnet by default for safe testing.

Connect A Wallet

Install Phantom, Solflare, Backpack, or another Solana Wallet Standard browser wallet. Switch the wallet to devnet before testing.

On Android Chrome or an Android Chrome PWA, install a compatible Solana mobile wallet such as Phantom, Solflare, or Seed Vault Wallet. Mobile Wallet Adapter can appear in the same wallet list after refreshWallets().

In Vue:

const { wallets, selectedWallet, refreshWallets, selectWallet } = useWallets();
const { publicKey, connected, connect, disconnect } = useWallet();

In Nuxt:

const { wallets, selectedWallet, refreshWallets, selectWallet } = useSolanaWallets();
const { publicKey, connected, connect, disconnect } = useSolanaWallet();

Select a wallet from wallets, then call connect(). Selecting a wallet only configures the active wallet; it does not connect it. Some extensions expose previously authorized accounts after a page refresh, but Vue Solana still keeps connected false until connect() succeeds.

When autoConnect is enabled, Vue Solana restores only the wallet identity the user selected previously and only after that wallet is discovered again on the client. It stores name, platform, and source metadata in localStorage, not private keys, sessions, or transactions.

iOS browser wallet support uses wallet-specific universal links because Mobile Wallet Adapter web support is Android-only. Phantom, Solflare, and Backpack appear in the same useWallets() list on iOS browsers.

Manual Wallet Testing

Use this checklist when validating a browser extension, Android MWA wallet, or iOS browser wallet by hand.

  1. Configure the app for devnet and verify the UI shows the devnet endpoint.
  2. Install a supported wallet and switch the wallet itself to devnet.
  3. Fund the wallet with devnet SOL from https://faucet.solana.com.
  4. Open the example app and click the wallet refresh action.
  5. Confirm the wallet appears in the unified wallet list with the expected source.
  6. Select the wallet and verify selection alone does not connect it.
  7. Click connect and approve the wallet prompt.
  8. Confirm the public key and connected state update after connect() resolves.
  9. Reload the page and confirm the previously selected wallet identity can be restored without arbitrary wallet selection.
  10. Disconnect and verify public key and connected state clear.

Expected wallet sources:

PlatformExpected sourceNotes
Desktop browser extensionwallet-standardPhantom, Solflare, Backpack, and other standard wallets can appear when installed.
Android Chrome or Chrome PWAmobile-wallet-adapterRequires a compatible native wallet and Android MWA browser support.
iOS browserdeep-linkPhantom, Solflare, and Backpack entries use wallet-specific universal links.
Desktop native appNot implemented yetDesktop native protocol links are not supported yet.

Sign A Message

Use message signing for wallet ownership checks or authentication challenges. It does not submit a transaction and does not authorize on-chain state changes.

In Vue:

const { connected, canSignMessage } = useWallet();
const signMessage = useSignMessage();

if (connected.value && canSignMessage.value) {
  await signMessage.execute(new TextEncoder().encode("Sign in to example.com"));
}

In Nuxt:

const { connected, canSignMessage } = useSolanaWallet();
const signMessage = useSolanaSignMessage();

if (connected.value && canSignMessage.value) {
  await signMessage.execute(new TextEncoder().encode("Sign in to example.com"));
}

Render a disabled auth button when canSignMessage is false. Some wallets can connect and sign transactions without supporting arbitrary message signing.

For manual testing, use a clear challenge string that includes your domain, a nonce, and an expiration time. Never ask users to sign blank or ambiguous messages.

const challenge = new TextEncoder().encode(
  "Sign in to example.com\nNonce: 8f1a2c\nExpires: 2026-07-03T12:00:00Z",
);

After signing, verify that the UI shows the returned signature bytes and does not treat the message signature as an on-chain transaction.

Send A Transfer

The Vue and Nuxt examples include recipient address and amount fields for a real transfer. They use devnet by default so you can test with SOL that has no real value. For mainnet, configure mainnet or a mainnet RPC endpoint and use a wallet with real SOL for fees.

Start with a tiny amount such as 0.000001 SOL while testing.

Browser apps that create or serialize transactions should initialize the framework package Buffer polyfill before transaction code:

import { installSolanaBufferPolyfill } from "@vue-solana/vue/buffer-polyfill";

installSolanaBufferPolyfill();

The wallet will prompt you to approve the transaction. After approval, the example shows the transaction signature, confirmation state, and explorer link. On Android Mobile Wallet Adapter, Vue Solana prefers wallet signing plus app-side RPC submission when supported, which makes the returned signature more reliable after the wallet redirects back to the browser.

For manual transfer testing:

  1. Keep both the app and wallet on devnet.
  2. Use a recipient address you control or a newly generated devnet wallet.
  3. Start with 0.000001 SOL.
  4. Review the wallet prompt before approval.
  5. After submission, wait for the example to show confirmation status.
  6. Open the explorer link and confirm it uses the devnet cluster query.
  7. Refresh the sender and recipient balances.

Explorer URLs should be cluster-aware:

function explorerUrl(signature: string, cluster: string) {
  const suffix = cluster === "mainnet" || cluster === "mainnet-beta" ? "" : `?cluster=${cluster}`;
  return `https://explorer.solana.com/tx/${signature}${suffix}`;
}

If confirmation times out after a signature is returned, do not immediately resubmit. Check the signature status or explorer first; the transaction may still confirm.

The client-send demo uses the official rpcTransactionPlanSendingExecutor() installed by the default client. Connect a wallet that supports signTransaction, fund the demo payer where the example requires it, and use useSolanaSendTransaction() or useSolanaSendTransactions(). The executor submits the transaction and waits for confirmed commitment before the composable reports sent; there is no separate wallet send popup. In Nuxt, create any demo payer in a client-only plugin rather than nuxt.config.ts, and set clientPlugin: false so the module skips its own plugin.

Final Verification

Before relying on an app flow, verify these behaviors on devnet:

  • RPC reads work without a wallet.
  • Wallet discovery shows only supported wallet sources for the current platform.
  • Wallet selection and connection are separate user actions.
  • Optional autoConnect restores only the previously selected wallet identity.
  • Unsupported message signing or transaction signing capabilities are disabled in the UI.
  • Message signing returns a signature without submitting an on-chain transaction.
  • Transfer submission returns a signature and confirmation status.
  • Client-sent transactions use the official planner and RPC plan-sending executor, require a configured payer signer, and reach sent only after confirmed commitment.
  • Explorer links point to the same cluster as the app.
  • mainnet is used only when you intentionally configure mainnet and understand that real SOL is at risk.

More Reading