@lukso/up-modal
v0.19.1
Published
Connect modal for LUKSO Universal Profiles
Readme
Table of contents
- What gets auto-detected
- Installation
- Minimal setup
- API reference
- Advanced
- Framework integrations
- Customization
- Internationalization
- License
What gets auto-detected
Modal automatically configures:
| App | How | | ---------------- | ------------------------------------------------------- | | UP Mobile | WalletConnect (deep link on mobile, QR code on desktop) | | UP Extension | Browser extension, detected via EIP-6963 | | EOA wallets | Other EIP-6963 wallets (MetaMask, Coinbase, etc.) |
Installation
# pnpm
pnpm add @lukso/up-modal
# npm
npm install @lukso/up-modal
# yarn
yarn add @lukso/up-modalAlso install the required peer dependencies:
npm install @wagmi/core viemMinimal setup
Initialize modal
import { setupLuksoConnector } from '@lukso/up-modal'
const connector = await setupLuksoConnector({
walletConnect: {
projectId: 'YOUR_REOWN_PROJECT_ID', // get yours at https://cloud.reown.com
},
})Open the Sign In modal
connector.showSignInModal()Open the Sign Up modal
connector.showSignUpModal()API reference
setupLuksoConnector
Returns Promise<LuksoConnector>. Call once at app initialization. Example with custom options:
import { luksoTestnet } from 'viem/chains'
const connector = await setupLuksoConnector({
theme: 'dark',
walletConnect: {
projectId: 'YOUR_REOWN_PROJECT_ID', // get yours at https://cloud.reown.com
},
chains: {
additional: [luksoTestnet], // add LUKSO Testnet
},
connectors: {
eoa: false,
},
onConnect: (event) => console.log('Connected:', event.detail),
onError: (event) => console.error('Error:', event.detail),
onClose: () => console.log('Modal closed'),
})Config options
| Option | Type | Default | Description |
| ------------------------- | ----------------------------- | ------------- | ----------------------------------------------------------------- |
| theme | 'light' \| 'dark' \| 'auto' | 'light' | Modal theme |
| onConnect | (e: CustomEvent) => void | — | Called on successful connection |
| onError | (e: CustomEvent) => void | — | Called on connection error |
| onClose | () => void | — | Called when modal is closed |
| walletConnect.enabled | boolean | true | Enable WalletConnect |
| walletConnect.projectId | string | LUKSO default | WalletConnect project ID |
| chains.defaultChainId | number | 42 | Default chain (LUKSO mainnet) |
| chains.additional | Chain[] (from viem) | [] | Extra chains alongside LUKSO mainnet (e.g. [luksoTestnet]) |
| chains.skipChainSwitch | boolean | false | Skip automatic chain switch on connect |
| storage.key | string | 'up-wagmi' | localStorage key prefix for wagmi state |
| connectors.upMobile | boolean | true | Show UP Mobile connector button |
| connectors.upExtension | boolean | true | Show UP Extension connector button |
| connectors.eoa | boolean | true | Show EOA wallets divider and button |
| wagmiConfig | Config (from @wagmi/core) | auto-created | Pass your own wagmi config |
LuksoConnector methods
| Method | Description |
| ------------------- | ------------------------------------------------------------------------------------ |
| showSignInModal() | Opens the sign-in modal (creates <connect-modal> in document.body on first call) |
| showSignUpModal() | Opens the sign-up modal for creating a new Universal Profile |
| closeModal() | Closes the modal |
| setTheme(theme) | Updates the theme ('light' \| 'dark' \| 'auto') — works while modal is open |
| destroyModal() | Closes the modal and removes the element from the DOM |
| wagmiConfig | The underlying wagmi Config instance for advanced use |
Advanced
Using your own wagmi config
import { setupLuksoConnector } from '@lukso/up-modal'
import { myWagmiConfig } from './wagmi'
const connector = await setupLuksoConnector({
wagmiConfig: myWagmiConfig,
})Watching connection state
import { watchConnection, getConnection } from '@wagmi/core'
const { wagmiConfig } = connector
// One-time read
const connection = getConnection(wagmiConfig)
// Subscribe to changes
const stopWatching = watchConnection(wagmiConfig, {
onChange: (connection) => {
console.log(connection.address, connection.chainId)
},
})Multi-chain support
In the default auto-setup configuration (when you don't provide a custom wagmiConfig), LUKSO mainnet is always included and cannot be removed from the supported networks. Any additional chains are added alongside it via chains.additional.
The following example adds Ethereum mainnet so users can connect from either network:
import { mainnet } from 'viem/chains'
import { setupLuksoConnector } from '@lukso/up-modal'
const connector = await setupLuksoConnector({
chains: {
additional: [mainnet],
},
})Testnets are also supported, just pass them in an array. Example of adding LUKSO Testnet and Ethereum Sepolia:
import { setupLuksoConnector } from '@lukso/up-modal'
import { luksoTestnet, sepolia } from 'viem/chains'
const connector = await setupLuksoConnector({
chains: {
additional: [luksoTestnet, sepolia],
},
})If a wallet connects on a chain that is not in this list, the modal will emit an
on-errorevent and the connection will be rejected — since wagmi has no RPC transport configured for that chain and any calls would fail.
Framework integrations
Vue 3
Full working example.
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { setupLuksoConnector, type LuksoConnector } from '@lukso/up-modal'
const connector = ref<LuksoConnector | null>(null)
onMounted(async () => {
connector.value = await setupLuksoConnector({ theme: 'light' })
})
</script>
<template>
<button :disabled="!connector" @click="connector?.showSignInModal()">
Connect
</button>
</template>Nuxt
Plugin (plugins/lukso.client.ts):
import { setupLuksoConnector } from '@lukso/up-modal'
import type { LuksoConnector } from '@lukso/up-modal'
let connector: LuksoConnector | null = null
export default defineNuxtPlugin(async () => {
if (process.server) return
connector = await setupLuksoConnector({
theme: 'light',
onConnect: (event) => console.log('Connected:', event.detail),
})
})
export { connector }Component:
<template>
<button @click="connector?.showSignInModal()">Connect Wallet</button>
</template>
<script setup lang="ts">
import { connector } from '~/plugins/lukso.client'
</script>React
Full working example.
import { setupLuksoConnector } from '@lukso/up-modal'
import type { LuksoConnector } from '@lukso/up-modal'
import { useEffect, useState } from 'react'
export function ConnectButton() {
const [connector, setConnector] = useState<LuksoConnector | null>(null)
useEffect(() => {
setupLuksoConnector({
theme: 'light',
onConnect: (event) => console.log('Connected:', event.detail),
}).then(setConnector)
}, [])
return (
<button disabled={!connector} onClick={() => connector?.showSignInModal()}>
Connect Wallet
</button>
)
}Watching connection state (requires @wagmi/core):
import { watchConnection } from '@wagmi/core'
const stopWatching = watchConnection(connector.wagmiConfig, {
onChange: (connection) => {
console.log('Address:', connection.address)
},
})Svelte
Initialization (src/lib/connector.svelte.ts):
import { setupLuksoConnector } from '@lukso/up-modal'
import type { LuksoConnector } from '@lukso/up-modal'
export const connector: { current: LuksoConnector | null } = $state({
current: null,
})
export async function initConnector() {
connector.current = await setupLuksoConnector({
theme: 'light',
onConnect: (e) => console.log('Connected:', e.detail),
})
}Component (ConnectButton.svelte):
<script lang="ts">
import { connector, initConnector } from '$lib/connector.svelte'
$effect(() => {
initConnector()
})
</script>
<button onclick={() => connector.current?.showSignInModal()}>
Connect Wallet
</button>Customization
The modal supports visual customization via CSS variables. Set them on :root or any ancestor of the <connect-modal> element.
CSS Variables
| Variable | Description | Default |
| ---------------------------- | -------------------------- | --------------------------------------------- |
| --up-modal-font-family | Modal font family | Inter, ui-sans-serif, system-ui, sans-serif |
| --up-modal-font-color | Modal text color (light) | #243542 |
| --up-modal-font-dark-color | Modal text color (dark) | #f5f8fa |
| --up-modal-btn-color | Button background (light) | #ffffff |
| --up-modal-btn-dark-color | Button background (dark) | #243542 |
| --up-modal-btn-text | Button text & icon (light) | #243542 |
| --up-modal-btn-dark-text | Button text & icon (dark) | #ffffff |
| --up-modal-btn-radius | Button border radius | 12px (large) / 10px (medium) |
| --up-modal-border-radius | Modal border radius | 12px |
| --up-modal-bg | Modal background (light) | #f8fafb |
| --up-modal-dark-bg | Modal background (dark) | #121b21 |
Example with custom brand colors
<style>
:root {
--up-modal-font-family: 'Roboto', sans-serif;
--up-modal-font-color: #1e3a5f;
--up-modal-btn-color: #4285f4;
--up-modal-btn-text: #ffffff;
--up-modal-bg: #eff6ff;
--up-modal-border-radius: 20px;
}
</style>Internationalization
The modal ships with English (en_US) translations by default. You can customize any text string or add support for other languages.
The i18n features require @lukso/core, a separate LUKSO utilities package. Install it alongside @lukso/up-modal:
npm install @lukso/coreTranslation keys
| Key | Default (English) |
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| connect_modal_title | Let's log you in |
| connect_modal_description | Log in with your Universal Profile |
| connect_modal_connectors_passkey_wallet | Passkey Wallet |
| connect_modal_connectors_up_mobile | Mobile Application |
| connect_modal_connectors_up_browser_extension | Browser Extension |
| connect_modal_or | Or |
| connect_modal_or_info | Log in with a different wallet |
| connect_modal_other_connectors | Connect Wallet |
| connect_modal_eoa_title | Connect your Wallet |
| connect_modal_installed | INSTALLED |
| connect_modal_failed_to_load | Failed to load... |
| connect_modal_try_again | Try again |
| connect_modal_qr_code_title | Scan to log in |
| connect_modal_qr_code_description | Scan the below QR code with the Universal Profile mobile app to log in |
| connect_modal_close | Close modal |
| connect_modal_go_back | Go back |
| sign_up_modal_title | Create Universal Profile! |
| sign_up_modal_description | Choose the device on which you want to create a profile. You can use either to interact with supported apps |
| sign_up_modal_use_mobile | Install the Mobile Application |
| sign_up_modal_install_extension | Install the Browser Extension |
Changing the locale
The modal uses the global IntlService from @lukso/core. If your app already sets up an intl service, the modal will use it automatically. Otherwise it creates a local English instance.
import { createIntlService, setIntlService } from '@lukso/core/services/intl'
// Create a service with your translations
const intl = createIntlService({
locale: 'de-DE',
messages: {
connect_modal_title: 'Wir melden Sie an',
connect_modal_description: 'Melden Sie sich mit Ihrem Universalprofil an.',
// ... other keys
},
})
// Set it globally — the modal picks it up automatically
setIntlService(intl)Overriding individual strings
If you only need to change a few labels, set the global service with your full message map. Any keys you omit will fall back to the built-in English translations.
import enMessages from '@lukso/core/translations/en_US.json'
const intl = createIntlService({
locale: 'en-US',
messages: {
...enMessages,
connect_modal_title: 'Sign in to MyApp',
connect_modal_description: 'Connect your Universal Profile to continue',
},
})
setIntlService(intl)Switching locale at runtime
import { getIntlService } from '@lukso/core/services/intl'
import germanMessages from '@lukso/core/translations/de_DE.json'
const intl = getIntlService()
intl?.setLocale('de-DE', germanMessages)
// The modal re-renders automaticallyLicense
Apache-2.0 — see LICENSE.md for details.
