Verified Badge
Verified check badge with a Name/Handle layout and palette overrides.
✓
Anish@aniui
Web preview — components render natively on iOS & Android
import {
VerifiedBadge,
VerifiedBadgeCheck,
VerifiedBadgeContent,
VerifiedBadgeName,
VerifiedBadgeHandle,
} from "@/components/ui/verified-badge";
export function MyScreen() {
return (
<VerifiedBadge>
<VerifiedBadgeCheck />
<VerifiedBadgeContent>
<VerifiedBadgeName>Anish Lawrence</VerifiedBadgeName>
<VerifiedBadgeHandle>@anishlp</VerifiedBadgeHandle>
</VerifiedBadgeContent>
</VerifiedBadge>
);
}Installation#
npx @aniui/cli add verified-badgeUsage#
import {
VerifiedBadge,
VerifiedBadgeCheck,
VerifiedBadgeContent,
VerifiedBadgeName,
VerifiedBadgeHandle,
} from "@/components/ui/verified-badge";
export function MyScreen() {
return (
<VerifiedBadge>
<VerifiedBadgeCheck />
<VerifiedBadgeContent>
<VerifiedBadgeName>Anish Lawrence</VerifiedBadgeName>
<VerifiedBadgeHandle>@anishlp</VerifiedBadgeHandle>
</VerifiedBadgeContent>
</VerifiedBadge>
);
}Source#
components/ui/verified-badge.tsx
import React, { createContext, useContext } from "react";
import { View, Text } from "react-native";
import { BadgeCheck } from "lucide-react-native";
import { cn } from "@/lib/utils";
export type VerifiedBadgePalette = {
surface: string;
border: string;
name: string;
handle: string;
check: string;
checkMark: string;
};
const DEFAULT_PALETTE: VerifiedBadgePalette = {
surface: "transparent",
border: "transparent",
name: "#18181b",
handle: "#71717a",
check: "#2563eb",
checkMark: "#ffffff",
};
export const VERIFIED_BADGE_EMERALD_PALETTE: VerifiedBadgePalette = {
...DEFAULT_PALETTE,
check: "#10b981",
};
export const VERIFIED_BADGE_INK_PALETTE: VerifiedBadgePalette = {
surface: "#131315",
border: "#27272a",
name: "#fafafa",
handle: "#a1a1aa",
check: "#fafafa",
checkMark: "#131315",
};
type VerifiedBadgeContextValue = {
palette: VerifiedBadgePalette;
size: "sm" | "md";
};
const VerifiedBadgeContext = createContext<VerifiedBadgeContextValue | null>(null);
function useVerifiedBadgeContext(part: string): VerifiedBadgeContextValue {
const ctx = useContext(VerifiedBadgeContext);
if (!ctx) {
throw new Error(`VerifiedBadge${part} must be rendered inside a <VerifiedBadge>`);
}
return ctx;
}
export interface VerifiedBadgeProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
palette?: Partial<VerifiedBadgePalette>;
size?: "sm" | "md";
label?: string;
}
export function VerifiedBadge({ className, palette, size = "md", label, style, children, ...props }: VerifiedBadgeProps) {
const resolved: VerifiedBadgePalette = { ...DEFAULT_PALETTE, ...palette };
const surfaceOverride = resolved.surface !== "transparent" ? resolved.surface : undefined;
const borderOverride = resolved.border !== "transparent" ? resolved.border : undefined;
return (
<VerifiedBadgeContext.Provider value={{ palette: resolved, size }}>
<View
className={cn(
"flex-row items-center rounded-full border border-border bg-secondary/50",
size === "sm" ? "gap-1 px-2 py-0.5" : "gap-1.5 px-2.5 py-1",
className
)}
style={[
surfaceOverride || borderOverride ? { backgroundColor: surfaceOverride, borderColor: borderOverride } : undefined,
style,
]}
accessibilityRole="text"
accessibilityLabel={label ?? "Verified"}
{...props}
>
{children}
</View>
</VerifiedBadgeContext.Provider>
);
}
export interface VerifiedBadgeContentProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
}
export function VerifiedBadgeContent({ className, ...props }: VerifiedBadgeContentProps) {
return <View className={cn("flex-col shrink", className)} {...props} />;
}
export interface VerifiedBadgeNameProps extends React.ComponentPropsWithoutRef<typeof Text> {
className?: string;
}
export function VerifiedBadgeName({ className, style, numberOfLines = 1, ...props }: VerifiedBadgeNameProps) {
const { palette, size } = useVerifiedBadgeContext("Name");
return (
<Text
className={cn("font-semibold text-foreground", size === "sm" ? "text-xs" : "text-sm", className)}
style={[{ color: palette.name }, style]}
numberOfLines={numberOfLines}
{...props}
/>
);
}
export interface VerifiedBadgeHandleProps extends React.ComponentPropsWithoutRef<typeof Text> {
className?: string;
}
export function VerifiedBadgeHandle({ className, style, numberOfLines = 1, ...props }: VerifiedBadgeHandleProps) {
const { palette, size } = useVerifiedBadgeContext("Handle");
return (
<Text
className={cn("text-muted-foreground", size === "sm" ? "text-[10px]" : "text-xs", className)}
style={[{ color: palette.handle }, style]}
numberOfLines={numberOfLines}
{...props}
/>
);
}
export interface VerifiedBadgeCheckProps extends React.ComponentPropsWithoutRef<typeof View> {
className?: string;
label?: string;
}
export function VerifiedBadgeCheck({ className, label, ...props }: VerifiedBadgeCheckProps) {
const { palette, size } = useVerifiedBadgeContext("Check");
const iconSize = size === "sm" ? 14 : 18;
return (
<View className={className} accessible accessibilityRole="image" accessibilityLabel={label ?? "Verified"} {...props}>
<BadgeCheck
size={iconSize}
fill={palette.check}
color={palette.checkMark}
strokeWidth={2.5}
strokeLinecap="round"
strokeLinejoin="round"
/>
</View>
);
}

