kali-decoder/midnight-skills

react-wallet-connector

Generate a React + Vite TypeScript app that connects to a Midnight wallet via the DApp Connector API (@midnight-ntwrk/dapp-connector-api).

First seen Jul 2, 2026

Installation

$ npx skills add kali-decoder/midnight-skills --skill react-wallet-connector

Summary

  • Generate a React + Vite TypeScript app that connects to a Midnight wallet via the DApp Connector API (@midnight-ntwrk/dapp-connector-api).
  • Use when building a React frontend, bootstrapping a wallet connector, wiring connect/disconnect UI, reading window.midnight wallets, getting unshielded addresses, or asking about the DApp Connector API connection flow.
  • Produces a minimal runnable template — styling is intentionally omitted so the user can add Tailwind, CSS modules, etc.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from kali-decoder/midnight-skills · top by installs.

npx skills add kali-decoder/midnight-skills

Browse all from kali-decoder/midnight-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 16
License LICENSE
Default branch main
Open issues 5
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,659 B
  • docs SUMMARY.md 509 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 117 installs

SKILL.md

React Wallet Connector Skill

This skill generates a complete, runnable React application that connects to a Midnight wallet extension using the DApp Connector API. It covers every file needed: Vite + React + TypeScript scaffold, wallet selection, connection logic, and a WalletCard UI component.

Primary references:

  • docs.midnight.network — React wallet connector guide (DApp Connector API)
  • llms.txt — full Midnight documentation index
  • @midnight-ntwrk/[email protected] — latest stable connector package (Feb 2026)

Key architecture notes:

  • Wallets inject InitialAPI instances on window.midnight, each keyed by a UUID — never use hardcoded keys like window.midnight.mnLace
  • Enumerate wallets with Object.values(window.midnight); when multiple wallets exist, let the user choose
  • Import @midnight-ntwrk/dapp-connector-api as a side effect to augment global window.midnight types
  • Request the shielded address only when your app actually needs it; the template uses getUnshieldedAddress()
  • Network IDs: 'undeployed' (local), 'preview', 'preprod', 'mainnet' — must match the wallet's configured network
  • Code examples omit CSS styling by design; add your preferred styling solution after scaffolding

Relationship to other skills:

  • For full contract deploy + circuit calls via 1AM wallet → use 1am-wallet/
  • For headless Node.js wallet + tests → use example-hello-world/ or midnight-js/
  • This skill is the frontend wallet connection foundation only

1) Project Structure

my-wallet-app/
├── index.html
├── package.json
├── tsconfig.json
├── tsconfig.app.json
├── tsconfig.node.json
├── vite.config.ts
└── src/
    ├── main.tsx
    ├── App.tsx
    ├── WalletCard.tsx
    ├── types.ts
    ├── selectWallet.ts
    └── vite-env.d.ts

2) Prerequisites

  • Node.js 18+ and npm
  • Basic React + TypeScript familiarity
  • A Midnight wallet browser extension installed (1AM, Lace, or any DApp Connector–compatible wallet)

3) Scaffold & Install

npm create vite@latest my-wallet-app -- --template react-ts
cd my-wallet-app
npm install @midnight-ntwrk/[email protected]

4) package.json

Use the Vite react-ts template output and ensure @midnight-ntwrk/dapp-connector-api is listed:

{
  "name": "my-wallet-app",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@midnight-ntwrk/dapp-connector-api": "4.0.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "@vitejs/plugin-react": "^4.3.0",
    "typescript": "~5.7.0",
    "vite": "^6.0.0"
  }
}

Pin @midnight-ntwrk/dapp-connector-api to 4.0.1 unless the user specifies otherwise. Check npm view @midnight-ntwrk/dapp-connector-api version if install fails with ETARGET.


5) vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
});

6) tsconfig.json

{
  "files": [],
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}

7) tsconfig.app.json

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["src"]
}

8) tsconfig.node.json

{
  "compilerOptions": {
    "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
    "target": "ES2022",
    "lib": ["ES2023"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true
  },
  "include": ["vite.config.ts"]
}

9) src/vite-env.d.ts

/// <reference types="vite/client" />

The @midnight-ntwrk/dapp-connector-api side-effect import in App.tsx augments window.midnight — no manual declaration needed.


10) src/types.ts

export interface WalletCardProps {
  isConnected: boolean;
  walletAddress: string | null;
  onConnect: () => void;
  onDisconnect: () => void;
}

11) src/selectWallet.ts

Wallets inject under window.midnight with UUID keys. Always enumerate — never hardcode a wallet name.

import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';

export const listWallets = (): InitialAPI[] => {
  const injected = window.midnight;
  return injected ? Object.values(injected) : [];
};

export const selectWallet = (): InitialAPI => {
  const wallets = listWallets();

  if (wallets.length === 0) {
    throw new Error(
      'No Midnight wallet found. Please install a Midnight wallet extension.',
    );
  }

  return wallets[0];
};

When more than one wallet is available, render a picker using listWallets() and let the user choose. Display each wallet's name and icon safely to prevent XSS.


12) src/WalletCard.tsx

Presentation layer — connection status, address display, connect/disconnect buttons. No CSS included; add styling as needed.

import React from 'react';
import type { WalletCardProps } from './types';

const WalletCard: React.FC<WalletCardProps> = ({
  isConnected,
  walletAddress,
  onConnect,
  onDisconnect,
}) => {
  return (
    <div>
      <div>
        <h2>Connection Status</h2>
        <div>{isConnected ? 'Connected' : 'Disconnected'}</div>
      </div>

      <div>
        {isConnected && walletAddress ? (
          <>
            <p>Wallet Address:</p>
            <p title={walletAddress}>{walletAddress}</p>
          </>
        ) : (
          <p>Please connect your wallet to proceed.</p>
        )}
      </div>

      <div>
        {isConnected ? (
          <button onClick={onDisconnect}>Disconnect Wallet</button>
        ) : (
          <button onClick={onConnect}>Connect Wallet</button>
        )}
      </div>
    </div>
  );
};

export default WalletCard;

13) src/App.tsx

Connection logic using the DApp Connector API.

import React, { useState } from 'react';
import WalletCard from './WalletCard';
import '@midnight-ntwrk/dapp-connector-api';
import { selectWallet } from './selectWallet';

const App: React.FC = () => {
  const [isConnected, setIsConnected] = useState<boolean>(false);
  const [walletAddress, setWalletAddress] = useState<string | null>(null);

  const handleConnect = async () => {
    console.log('Connect button clicked');
    let connected = false;
    let address: string | null = null;

    try {
      const wallet = selectWallet();

      // 'undeployed' for local dev; 'preprod' | 'preview' | 'mainnet' for live networks
      const connectedApi = await wallet.connect('preprod');

      const { unshieldedAddress } = await connectedApi.getUnshieldedAddress();
      address = unshieldedAddress;

      const serviceUriConfig = await connectedApi.getConfiguration();
      console.log('Service URI Config:', serviceUriConfig);

      const connectionStatus = await connectedApi.getConnectionStatus();
      if (connectionStatus.status === 'connected') {
        connected = true;
        console.log('Connected to the wallet:', address);
      }
    } catch (error) {
      console.log('An error occurred:', error);
    }

    setIsConnected(connected);
    setWalletAddress(address);
  };

  const handleDisconnect = () => {
    setWalletAddress(null);
    setIsConnected(false);
  };

  return (
    <div>
      <header>
        <h1>Midnight Wallet Connector</h1>
      </header>
      <main>
        <WalletCard
          isConnected={isConnected}
          walletAddress={walletAddress}
          onConnect={handleConnect}
          onDisconnect={handleDisconnect}
        />
      </main>
    </div>
  );
};

export default App;

Connection flow breakdown

  1. Select walletselectWallet() reads window.midnight entries; throws if none found (caught by try/catch)
  2. Connect to networkwallet.connect(networkId) prompts the user to authorize
  3. Retrieve addressgetUnshieldedAddress() returns the public unshielded address
  4. Check statusgetConnectionStatus() resolves to { status: 'connected' | 'disconnected' }
  5. Optional configgetConfiguration() returns service URIs (indexer, node, etc.) for downstream SDK wiring

14) src/main.tsx

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

15) index.html

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Midnight Wallet Connector</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

16) Run

npm install
npm run dev

Open http://localhost:5173. Click Connect Wallet — the extension prompts for authorization. After approval, the app shows connection status and the unshielded address.


17) Multi-Wallet Picker (Optional Enhancement)

When listWallets() returns more than one entry, replace selectWallet() auto-pick with a user-facing selector:

// src/WalletPicker.tsx
import type { InitialAPI } from '@midnight-ntwrk/dapp-connector-api';
import { listWallets } from './selectWallet';

type Props = {
  onSelect: (wallet: InitialAPI) => void;
};

export function WalletPicker({ onSelect }: Props) {
  const wallets = listWallets();

  if (wallets.length === 0) {
    return <p>No Midnight wallet found. Install a wallet extension and refresh.</p>;
  }

  return (
    <ul>
      {wallets.map((wallet) => (
        <li key={wallet.name}>
          <button type="button" onClick={() => onSelect(wallet)}>
            {wallet.name}
          </button>
        </li>
      ))}
    </ul>
  );
}

Render wallet name and icon as text only unless you sanitize HTML. Never use dangerouslySetInnerHTML for wallet-provided icon URLs.


18) Troubleshooting

Error Cause Fix
window.midnight is undefined No extension installed or page loaded before injection Install a Midnight wallet; refresh after install
No Midnight wallet found Extension disabled or wrong detection pattern Use Object.values(window.midnight) — not window.midnight.mnLace or other hardcoded keys
Connection rejected / fails Network mismatch Ensure connect('preprod') matches the wallet's active network
Connection hangs Wallet locked or not synced Unlock wallet; wait for sync
ETARGET on install Stale package version Run npm view @midnight-ntwrk/dapp-connector-api version and pin the latest
Type errors on window.midnight Missing side-effect import Add import '@midnight-ntwrk/dapp-connector-api' in App.tsx

19) Next Steps

After a working connector, extend the app:

Feature Skill / API
Deploy & call Compact contracts 1am-wallet/
Token transfers token-transfers/
Query balances & tx history indexer/
Sign arbitrary messages connectedApi signing methods (see DApp Connector API docs)
Multi-network selector Network dropdown calling wallet.connect(selectedNetwork)
Full SDK provider wiring midnight-js/

20) Agent Checklist

When generating this template for a user:

  • Scaffold with Vite react-ts or write all files from this skill
  • Install @midnight-ntwrk/[email protected]
  • Create types.ts, selectWallet.ts, WalletCard.tsx, App.tsx, main.tsx
  • Include side-effect import @midnight-ntwrk/dapp-connector-api in App.tsx
  • Use Object.values(window.midnight) for wallet detection — never hardcode wallet keys
  • Default network to 'preprod' unless user specifies local ('undeployed') or other network
  • Omit CSS unless the user requests styling
  • Mention wallet extension must be installed before testing