chainlyt
v0.1.1
Published
Responsive wallet-based leaderboard widget for React apps and plain browser embeds.
Maintainers
Readme
chainlyt
Responsive wallet-based gamification widgets for React apps and plain browser embeds.
What it supports
- Workspace leaderboard ranked by points
- Wallet profile with points balance and minted certificates
- Backend API integration with
baseUrl,apiKey, andworkspaceId - Spin-the-wheel rewards
- Daily check-in streak rewards
- Theme overrides for colors, radius, shadow, and font family
- React/Vite/Next.js usage
- Plain
<script>mounting for non-React sites
Install
npm install chainlyt react react-domReact usage
import {
LeaderboardWidget,
UserProfileWidget,
createChainlytConfigFromEnv,
} from 'chainlyt';
const api = createChainlytConfigFromEnv(import.meta.env);
export function Example() {
return (
<>
<LeaderboardWidget api={api} maxItems={10} />
<UserProfileWidget api={api} walletId="0x1234567890abcdef1234567890abcdef12345678" />
</>
);
}Vite .env
VITE_CHAINLYT_API_URL=http://localhost:8000
VITE_CHAINLYT_API_KEY=replace-with-workspace-api-key
VITE_CHAINLYT_WORKSPACE_ID=replace-with-workspace-idPass the wallet id from your frontend state:
const [walletId, setWalletId] = useState('');
<UserProfileWidget api={api} walletId={walletId} />Next.js usage
Use the widget in a client component:
'use client';
import { LeaderboardWidget, UserProfileWidget } from 'chainlyt';
export default function LeaderboardSection() {
return (
<>
<LeaderboardWidget
loadEntries={async () => {
const response = await fetch('/api/chainlyt/leaderboard');
return response.json();
}}
/>
<UserProfileWidget
loadProfile={async () => {
const response = await fetch('/api/chainlyt/profile?walletId=0x123...');
return response.json();
}}
/>
</>
);
}Backend endpoints
The package can call these widget-facing endpoints directly:
GET /widgets/workspaces/:workspaceId/leaderboardGET /widgets/workspaces/:workspaceId/wallets/:walletId/profile
Both expect X-API-Key.
Browser mounts
window.ChainlytLeaderboard.mount(...)window.ChainlytSpinWheel.mount(...)window.ChainlytDailyCheckIn.mount(...)window.ChainlytUserProfile.mount(...)
Plain script-tag usage
Ship the browser bundle from your package publish, or load it from a CDN that serves npm package files.
The published browser entry is exposed as chainlyt/browser.global.js.
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Leaderboard Embed</title>
<script crossorigin src="https://unpkg.com/react@19/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@19/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/chainlyt/browser.global.js"></script>
</head>
<body style="margin:0;padding:24px;background:#07111f;">
<div id="leaderboard-root"></div>
<script>
window.ChainlytLeaderboard.mount({
target: '#leaderboard-root',
organizationName: 'Chainloyalty',
walletIds: [
'0xA1B2C3D4E5F607182930AABBCCDDEEFF00112233',
'0x99887766554433221100AABBCCDDEEFF99887766'
],
entries: [
{
walletId: '0xA1B2C3D4E5F607182930AABBCCDDEEFF00112233',
displayName: 'Wallet Alpha',
score: 18420,
metrics: { txns: 241, volume: '42.8 ETH' }
},
{
walletId: '0x99887766554433221100AABBCCDDEEFF99887766',
displayName: 'Wallet Beta',
score: 15120
}
],
theme: {
accent: '#ffd166',
accentText: '#221200'
}
});
</script>
</body>
</html>Main props
Data
entries: local array of wallet leaderboard entriesloadEntries: async loader for API-backed dataapi: backend config for built-in fetch supportwalletIds: allow-list of wallet IDs to show for the orgorganizationId: optional org filter if your entries containorganizationIdorganizationName: badge label in the headermaxItems: how many ranked rows to render
UI customization
theme: color and surface tokensstrings: copy overrideslayout:'auto' | 'table' | 'cards'compact: hide extra metric text in the card layoutrenderIdentity: custom wallet cell rendererrenderScore: custom score pill renderercolumns: replace the default wallet / score / metrics columns
Entry shape
type LeaderboardEntry = {
walletId: string;
score: number;
organizationId?: string;
organizationName?: string;
displayName?: string;
avatarUrl?: string;
href?: string;
metrics?: Record<string, number | string>;
};Profile extras
UserProfileWidget now supports:
walletIdwithapifor backend loadingcertificateson theprofileobjectcreateChainlytConfigFromEnv(import.meta.env)
Test locally
cd packages/leaderboard-widget
npm run typecheck
npm run build
npm packInstall the generated tarball in another app before publishing:
npm install /absolute/path/to/chainlyt-0.1.0.tgzPublish
cd packages/leaderboard-widget
npm login
npm run build
npm pack
npm publishNotes:
npm packis optional, but useful to inspect the exact tarball before publishing.npm publishalso runsprepublishOnly, which triggersnpm run typecheck && npm run build.- Make sure the npm package name
chainlytis available to your account before publishing.
Production Security
Do not expose a workspace API key in browser env vars for production usage.
- Safe: publishing the package, using MetaMask in the frontend, and rendering the widgets in your app.
- Not safe: shipping
VITE_CHAINLYT_API_KEYto the browser and calling the widget backend directly from client-side code.
Recommended production setup:
- Keep the workspace API key on your backend only.
- Let the frontend connect MetaMask and send the wallet address to your backend.
- Have your backend call the widget endpoints or internal services, then return only the allowed leaderboard/profile data to the browser.
