Back to Audio
Components / Audio
Ios Earbuds
Free Audio React component — IosEarbudsWidget. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.
AirPods-style widget showing device name and connection status. Small, familiar, and perfect beside other iOS-style controls.
Preview
Audio
ConnectedAirPods Pro
Connected
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/audio/ios-earbuds-widget.tsxin your project. - 4
Import and render:
Exampleimport { IosEarbudsWidget } from "@/components/audio/ios-earbuds-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/audio/ios-earbuds-widget.tsx
"use client";import { forwardRef, type ComponentPropsWithoutRef } from "react";import { cn } from "@/lib/cn";// Inline SVG — left and right bud groups with gradient fills for a subtle 3D lookfunction AirPodsPair() {return (<svg viewBox="0 0 120 80" className="h-19 w-27" aria-hidden><defs><linearGradient id="earbuds-body" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stopColor="#FFFFFF" /><stop offset="100%" stopColor="#E8E8ED" /></linearGradient><linearGradient id="earbuds-stem" x1="0%" y1="0%" x2="0%" y2="100%"><stop offset="0%" stopColor="#F5F5F7" /><stop offset="100%" stopColor="#D1D1D6" /></linearGradient></defs><g transform="translate(14 4)"><pathd="M28 6 C38 6 44 14 44 24 C44 34 38 40 28 40 C18 40 12 34 12 24 C12 14 18 6 28 6 Z"fill="url(#earbuds-body)"stroke="#D1D1D6"strokeWidth="0.75"/><ellipse cx="28" cy="24" rx="8" ry="10" fill="#E5E5EA" opacity="0.45" /><rectx="24"y="38"width="8"height="26"rx="4"fill="url(#earbuds-stem)"stroke="#C7C7CC"strokeWidth="0.5"/><rect x="25" y="58" width="6" height="4" rx="2" fill="#AEAEB2" /></g><g transform="translate(58 4)"><pathd="M28 6 C38 6 44 14 44 24 C44 34 38 40 28 40 C18 40 12 34 12 24 C12 14 18 6 28 6 Z"fill="url(#earbuds-body)"stroke="#D1D1D6"strokeWidth="0.75"/><ellipse cx="28" cy="24" rx="8" ry="10" fill="#E5E5EA" opacity="0.45" /><rectx="24"y="38"width="8"height="26"rx="4"fill="url(#earbuds-stem)"stroke="#C7C7CC"strokeWidth="0.5"/><rect x="25" y="58" width="6" height="4" rx="2" fill="#AEAEB2" /></g></svg>);}// name — device label shown at the bottom; connected — drives the status dot and subtitleexport type IosEarbudsWidgetProps = Readonly<{// Device label shown at the bottom of the card.name?: string;// When true, shows a connected state with a green status dot.connected?: boolean;} & ComponentPropsWithoutRef<"div">>;// iOS-style earbuds card: header, AirPods illustration, device name + connection stateexport const IosEarbudsWidget = forwardRef<HTMLDivElement,IosEarbudsWidgetProps>(({ className, name = "AirPods Pro", connected = true, ...props }, ref) => (<divref={ref}data-slot="minimal-earbuds-widget"className={cn("flex h-44 w-44 max-w-full flex-col overflow-hidden rounded-3xl border border-neutral-100 bg-white p-4 font-sans shadow-lg shadow-black/5 select-none",className,)}{...props}><div className="flex items-center justify-between gap-2"><p className="text-[10px] font-semibold tracking-widest text-neutral-400 uppercase">Audio</p><spanaria-hiddenclassName={cn("h-2 w-2 shrink-0 rounded-full",connected ? "bg-green-500" : "bg-neutral-300",)}/><span className="sr-only">{connected ? "Connected" : "Disconnected"}</span></div><div className="flex flex-1 items-center justify-center"><AirPodsPair /></div><div className="text-center"><p className="text-[12px] font-semibold text-neutral-900">{name}</p><p className="mt-0.5 text-[10px] font-medium text-neutral-400">{connected ? "Connected" : "Tap to connect"}</p></div></div>));IosEarbudsWidget.displayName = "IosEarbudsWidget";