Components / Bluetooth
Bluetooth Face
Free Bluetooth React component — BluetoothFaceWidget. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.
A teal mascot face that reacts when Bluetooth connects or drops. Toggle on/off with an expression change — playful, but still reads as a real status widget.
Preview
Setup
How to use
Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.
- 1
Run in your terminal:
$npm install clsx tailwind-merge lucide-react - 2
Copy
lib/cn.tsbelow. Skip if you already havecn(). - 3
Copy the code below and create
components/bluetooth/bluetooth-face-widget.tsxin your project. - 4
Import and render:
Exampleimport { BluetoothFaceWidget } from "@/components/bluetooth/bluetooth-face-widget";
lib/cn.ts
import { type ClassValue, clsx } from "clsx";import { twMerge } from "tailwind-merge";export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs));}
components/bluetooth/bluetooth-face-widget.tsx
"use client";import { forwardRef, useState, type ComponentPropsWithoutRef } from "react";import { cn } from "@/lib/cn";import { Bluetooth } from "lucide-react";const MASCOT = "#14B8A6";const ACCENT = "#22D3EE";const DIAL = { cx: 88, cy: 84, r: 56 } as const;type BluetoothMascotProps = Readonly<{on: boolean;}>;function BluetoothMascot({ on }: BluetoothMascotProps) {return (<g aria-hidden><circle cx="88" cy="142" r="58" fill={MASCOT} /><circle cx="68" cy="122" r="5" fill="white" /><circle cx="108" cy="122" r="5" fill="white" /><circle cx="69" cy="123" r="2.2" fill="#1C1C1E" /><circle cx="109" cy="123" r="2.2" fill="#1C1C1E" />{on ? (<pathd="M76 138 Q88 146 100 138"stroke="white"strokeWidth="2.2"fill="none"strokeLinecap="round"/>) : (<linex1="76"y1="139"x2="100"y2="139"stroke="white"strokeWidth="2.2"strokeLinecap="round"/>)}</g>);}// Bluetooth toggle face widget — tap the mascot dial to turn Bluetooth on or off.export type BluetoothFaceWidgetProps = Readonly<{deviceName?: string;defaultOn?: boolean;} & ComponentPropsWithoutRef<"button">>;export const BluetoothFaceWidget = forwardRef<HTMLButtonElement,BluetoothFaceWidgetProps>(({className,deviceName = "Bluetooth",defaultOn = true,onClick,...props},ref,) => {const [on, setOn] = useState(defaultOn);return (<buttonref={ref}type="button"aria-pressed={on}aria-label={on ? `Turn ${deviceName} off` : `Turn ${deviceName} on`}data-slot="bluetooth-face-widget"onClick={(event) => {setOn((prev) => !prev);onClick?.(event);}}className={cn("relative h-44 w-44 max-w-full cursor-pointer overflow-hidden rounded-[1.75rem] bg-black font-sans shadow-lg shadow-black/5 select-none",className,)}{...props}><svgviewBox="0 0 176 176"className="absolute inset-0 h-full w-full"aria-hidden><rect width="176" height="176" fill="#000" /><BluetoothMascot on={on} /><circlecx={DIAL.cx}cy={DIAL.cy}r={DIAL.r}fill="none"stroke={on ? ACCENT : "white"}strokeWidth="1.5"opacity={on ? 0.45 : 0.25}/></svg><span className="absolute top-3.5 right-3.5 z-10" aria-hidden><Bluetooth size={14} color={on ? ACCENT : "#FFFFFF66"} /></span><div className="absolute inset-x-0 top-[32%] z-10 px-3 text-center"><pclassName={cn("text-base leading-none font-semibold",on ? "text-white" : "text-white/40",)}>{on ? "On" : "Off"}</p></div></button>);},);BluetoothFaceWidget.displayName = "BluetoothFaceWidget";