Templates
Components
Drop-in React components for platforms integrating Buff. Fully configurable — change any value and all widgets update live.
Configure all widgets
$
$
Buff Toggle
Let users enable/disable Buff and pick their plan. Toggle it, change plans — the round-up preview below updates instantly.
$
Buff Round-Up
Auto-invest into BTC
Rounds to $0.10Buff fee 0.75%
BuffToggle.tsx
typescript
1"use client"2import { useState } from "react"3import type { Buff, PlanTier } from "@buff/sdk"45interface BuffToggleProps {6 buff: Buff | null7 onToggle: (enabled: boolean) => void8 onPlanChange: (plan: PlanTier) => void9 defaultPlan?: PlanTier10 investAsset?: string11}1213export function BuffToggle({14 buff, onToggle, onPlanChange,15 defaultPlan = "sprout", investAsset = "BTC"16}: BuffToggleProps) {17 const [enabled, setEnabled] = useState(true)18 const [plan, setPlan] = useState(defaultPlan)1920 const plans = [21 { key: "seed", label: "Seed", roundTo: 0.05 },22 { key: "sprout", label: "Sprout", roundTo: 0.10 },23 { key: "tree", label: "Tree", roundTo: 0.50 },24 { key: "forest", label: "Forest", roundTo: 1.00 },25 ]2627 const toggle = () => {28 const next = !enabled29 setEnabled(next)30 onToggle(next)31 }3233 const changePlan = (key: string) => {34 setPlan(key as PlanTier)35 onPlanChange(key as PlanTier)36 if (buff) buff.setPlan(key as any)37 }3839 return (40 <div className="rounded-xl border p-4 bg-card">41 <div className="flex items-center justify-between mb-3">42 <div>43 <div className="text-sm font-medium">Buff Round-Up</div>44 <div className="text-xs text-muted-foreground">45 Auto-invest into {investAsset}46 </div>47 </div>48 <button onClick={toggle} className={`w-12 h-7 rounded-full49 ${enabled ? "bg-amber-500" : "bg-gray-600"}`}>50 <div className={`w-5 h-5 rounded-full bg-white transform51 ${enabled ? "translate-x-[22px]" : "translate-x-[3px]"}`} />52 </button>53 </div>54 {enabled && (55 <div className="grid grid-cols-4 gap-1">56 {plans.map((p) => (57 <button key={p.key}58 onClick={() => changePlan(p.key)}59 className={`text-xs py-2 rounded-lg font-medium60 ${plan === p.key61 ? "bg-amber-500/10 text-amber-500 border border-amber-500/20"62 : "text-muted-foreground hover:bg-accent"63 }`}>64 {p.label}65 </button>66 ))}67 </div>68 )}69 </div>70 )71}Round-Up Preview
Shows users exactly what their round-up will be. Change the tx value in the configurator above to see it update.
This transaction
Tx value$47.83
Rounded to$47.90
Round-up$0.07
You invest$0.0695 → BTC
Buff fee (0.75%)$0.0005
RoundUpPreview.tsx
typescript
1"use client"2import type { FeeBreakdown } from "@buff/sdk"34interface RoundUpPreviewProps {5 breakdown: FeeBreakdown | null6 asset: string7}89export function RoundUpPreview({ breakdown, asset }: RoundUpPreviewProps) {10 if (!breakdown || breakdown.skipped) {11 return (12 <div className="rounded-xl border p-4 bg-card text-center">13 <p className="text-sm text-muted-foreground">No round-up needed</p>14 </div>15 )16 }1718 return (19 <div className="rounded-xl border p-4 bg-card">20 <div className="flex justify-between text-sm mb-2">21 <span>Tx value</span>22 <span>${breakdown.txValueUsd.toFixed(2)}</span>23 </div>24 <div className="flex justify-between text-sm mb-2">25 <span>Rounded to</span>26 <span>${breakdown.roundedToUsd.toFixed(2)}</span>27 </div>28 <hr className="my-2" />29 <div className="flex justify-between font-semibold text-amber-500">30 <span>Investing</span>31 <span>${breakdown.roundUpUsd.toFixed(2)} → {asset}</span>32 </div>33 <div className="flex justify-between text-xs text-muted-foreground mt-1">34 <span>Buff fee ({breakdown.buffFeePercent}%)</span>35 <span>${breakdown.buffFeeUsd.toFixed(4)}</span>36 </div>37 </div>38 )39}4041// Usage:42// const breakdown = await buff.previewFees(txValueUsd)43// <RoundUpPreview breakdown={breakdown} asset="BTC" />Portfolio Card
Displays portfolio with allocation bar, holdings breakdown, and progress toward next swap. Change asset and threshold above.
Buff Portfolio
+18.6%
$82.80
142 round-ups
BTC85%
$70.38SOL15%
$12.42Next swap at $5$1.06 / $5
BuffPortfolio.tsx
typescript
1"use client"2import { useState, useEffect } from "react"3import type { Buff, Portfolio } from "@buff/sdk"45interface BuffPortfolioProps {6 buff: Buff | null7 refreshInterval?: number // ms, default 600008}910export function BuffPortfolio({ buff, refreshInterval = 60000 }: BuffPortfolioProps) {11 const [portfolio, setPortfolio] = useState<Portfolio | null>(null)1213 useEffect(() => {14 if (!buff) return15 const fetch = () => buff.getPortfolio().then(setPortfolio)16 fetch()17 const interval = setInterval(fetch, refreshInterval)18 return () => clearInterval(interval)19 }, [buff, refreshInterval])2021 if (!portfolio) return <div className="animate-pulse h-40 rounded-xl bg-card" />2223 const total = portfolio.totalUsd24 const plan = buff?.getCurrentPlan()2526 return (27 <div className="rounded-xl border p-5 bg-card">28 <div className="text-2xl font-bold mb-1">${total.toFixed(2)}</div>29 <div className="text-xs text-muted-foreground mb-4">30 {portfolio.pendingSol.toFixed(4)} SOL pending31 ({" "}32 ${portfolio.pendingUsd.toFixed(2)} / ${plan?.investThreshold ?? 5}33 {" "})34 </div>35 {portfolio.balances.map((b) => (36 <div key={b.asset} className="flex justify-between text-sm py-1.5 border-t">37 <span className="font-medium">{b.asset}</span>38 <span>${b.usdValue.toFixed(2)}</span>39 </div>40 ))}41 </div>42 )43}Stats Card
Lifetime statistics. The Buff fees shown reflect the current plan's fee rate.
Lifetime Stats
142
Round-ups
$82.80
Total invested
$0.62
Buff fees paid
BTC
Investing into
Sprout
Plan
2h ago
Last swap
BuffStats.tsx
typescript
1"use client"2import type { Buff } from "@buff/sdk"34interface BuffStatsProps {5 buff: Buff | null6}78export function BuffStats({ buff }: BuffStatsProps) {9 if (!buff) return null1011 const stats = buff.getStats()12 const plan = buff.getCurrentPlan()1314 return (15 <div className="rounded-xl border p-5 bg-card">16 <div className="grid grid-cols-2 gap-4">17 <Stat label="Round-ups" value={stats.totalRoundUps.toString()} />18 <Stat label="Total invested" value={`$${stats.totalInvestedUsd.toFixed(2)}`} />19 <Stat label="Buff fees" value={`$${stats.totalBuffFeesUsd.toFixed(2)}`} />20 <Stat label="Plan" value={plan.tier} />21 <Stat label="Investing into" value={plan.investInto} />22 <Stat label="Threshold" value={`$${plan.investThreshold}`} />23 </div>24 </div>25 )26}2728function Stat({ label, value }: { label: string; value: string }) {29 return (30 <div>31 <div className="text-lg font-bold">{value}</div>32 <div className="text-xs text-muted-foreground">{label}</div>33 </div>34 )35}Note
All widgets are controlled — they respond to props from the configurator. In your app, connect them to the real Buff instance. The code samples show exactly how. Styling uses basic Tailwind — customize to match your design system.