@livo-build/kit
v0.2.3
Published
Livo frontend kit — reusable React + wagmi v3 building blocks (wallet connect modal, providers, hooks) for web3 apps built on Livo.
Downloads
962
Maintainers
Readme
@livo-build/kit
Livo's frontend kit — reusable React + wagmi v3 building blocks
for web3 apps built on Livo. The frontend counterpart to
@livo-build/runtime (the backend stdlib). Zero runtime
dependencies of its own; React, wagmi, viem and @tanstack/react-query are peers.
Install
npm install @livo-build/kit
# peers (a Livo scaffold already has these):
npm install wagmi viem @tanstack/react-queryQuick start
import { LivoWeb3Provider, ConnectWallet } from "@livo-build/kit";
import { wagmiConfig } from "./wagmi"; // your chains + connectors (stays in your app)
export default function App() {
return (
<LivoWeb3Provider config={wagmiConfig}>
<ConnectWallet />
</LivoWeb3Provider>
);
}ConnectWallet is a button that opens a responsive wallet-picker modal
(centered on desktop, a bottom sheet on mobile) and turns into an account pill
(copy / disconnect) once connected.
Not opinionated — style it your way
The kit owns behaviour + structure + accessibility; the look is yours. Defaults
are deliberately neutral (they inherit the app font and lean grayscale) so apps don't
all look the same. Restyle via CSS variables, a per-part classNames prop, the class
hooks / [data-state] attributes, or go fully headless on the hooks. See
STYLING.md. Don't ship the neutral default as-is for a real product —
spend a moment making it match your app.
Customize (three levels)
- Restyle — override the CSS variables (
--wm-accent,--wm-radius,--wm-row, …) on:rootor any parent. No logic touched. - Custom trigger — keep the modal, supply your own button:
<ConnectWallet> {({ open, isConnected, address }) => ( <button onClick={open}>{isConnected ? address : "Sign in"}</button> )} </ConnectWallet> - Fully custom UI — build your own on the controlled
<WalletModal open onClose/>and the headlessuseWalletConnectors()hook (deduped connectors + connect / pending / error wiring).
Transactions
The whole write lifecycle (submit → pending → confirming → success/error) in one
component, with toasts + an explorer link (wrap your app in ToastProvider):
import { ToastProvider, TxButton } from "@livo-build/kit";
import { addresses, abis } from "./livo/contracts"; // generated by Livo
<ToastProvider>
<TxButton address={addresses.Counter} abi={abis.Counter} functionName="increment">
Increment
</TxButton>
</ToastProvider>Headless version: const { send, status, hash, error } = useTx({ address, abi, functionName }).
decodeRevert(error) turns a viem error into a short human message.
Contracts (bound to Livo's generated bindings)
import { createLivoContracts } from "@livo-build/kit";
import { addresses, abis } from "./livo/contracts"; // generated by sync_contract_bindings
export const contracts = createLivoContracts({ addresses, abis });
const n = contracts.useRead<bigint>("Counter", "number"); // read (chain-aware)
<TxButton {...contracts.useContract("Counter")} functionName="increment">+1</TxButton> // writeData, web3 primitives, UI
const { data } = useApi<{ ok: boolean }>("/health"); // same-origin /api/*
const { data } = useSubgraph({ url, query: GET_ITEMS }); // your indexer's graphql_url_latest
<Address address={addr} explorer /> <Balance address={addr} />
<TokenAmountInput value={v} onChange={setV} decimals={18} max={bal} symbol="USDC" />
<NetworkGuard chainId={11155111} name="Sepolia">{/* on-chain UI */}</NetworkGuard>
<Dialog open={open} onClose={close} title="Confirm">…</Dialog>
<Card/> <Skeleton width={120}/> <Spinner/> <EmptyState title="Nothing yet"/> <CopyButton value={addr}/>Theme it from one object
<KitThemeProvider theme={{ accent: "#6d28d9", radius: 16 }}>
{/* every kit component beneath inherits the brand */}
</KitThemeProvider>Accounts, tokens, approvals
<RequireConnection>{/* shown only when connected; else a connect prompt */}</RequireConnection>
<Connected><Identity address={addr} /></Connected> <Avatar address={addr} />
const { symbol, decimals } = useToken(token);
const { enough } = useAllowance({ token, owner, spender });
<ApproveButton token={token} spender={spender} amount={amt} /> // approve → toast → doneLink a wallet to Telegram
For apps that have both a Telegram bot and a web instance — verify the same person on both.
<LinkTelegramButton /> // Mini App: uses verified initData; web: bot deep-link
const { status, telegram, link } = useTelegramLink(); // headless
const mini = useTelegramMiniApp(); // { available, initData, user } when open inside TelegramThe hook talks to your api/ (same-origin), which verifies both proofs with
@livo-build/runtime (verifyTelegramInitData + a signed-nonce check) and writes
the binding to D1 (TelegramLinks). Endpoints it expects under endpoint (default
/api/telegram): GET /nonce, GET /status, POST /link (Mini App), POST /start
(web deep-link), POST /unlink. (Livo's telegram-app blueprint scaffolds these.)
Exports
- wallet —
ConnectWallet,WalletModal,useWalletConnectors - telegram —
LinkTelegramButton,useTelegramLink,useTelegramMiniApp - provider —
LivoWeb3Provider - contracts —
createLivoContracts,useContractValue,useContractEvent,resolveAddress - tx —
TxButton,useTx,decodeRevert - toast —
ToastProvider,useToast - data —
useApi,apiClient,useSubgraph,Async - token —
useToken,useAllowance,useApprove,ApproveButton - account —
Connected,Disconnected,RequireConnection,useIsConnected,Avatar,Identity - web3 —
Address,Balance,NetworkGuard,TokenAmountInput,ChainSwitcher,AddressInput - ui —
Dialog,Card,Skeleton,Spinner,EmptyState,CopyButton,Button,Badge - theme —
KitThemeProvider - hooks —
useCopyToClipboard,useLocalStorage,useDebounce,useInterval - format —
shortAddress,shortHash,formatToken,parseToken,formatUsd,timeAgo,avatarGradient
Every component is neutral by default — style it (see STYLING.md).
Versioning
Published from packages/kit via publish-kit.yml on merge to main (idempotent).
Livo scaffolds pin the version from convex/lib/frontendVersion.ts (KIT_VERSION).
