VibeUI
ComponentsDocs
VibeUI

Components with taste, that you own. Copy the source, retint the tokens, keep every line.

Library

  • All components
  • Signature pieces
  • Text effects
  • Footers

Docs

  • Introduction
  • Installation
  • Theming
  • CLI

Project

  • GitHub
  • Agents and MCP
  • Issues
  • MIT license
© 2026 VibeUI · MIT licensed
  1. Components
  2. Hover effects
  3. Magnetic Link

Magnetic Link

Link that drifts toward the cursor, with an arrow that nudges out.

Hover effectsClient component
Read the docsView source

Install

bash
npx @agents-npm-packages/vibeui add magnetic-link --dir ./src/components/ui

Import

tsx
import { MagneticLink } from "@/components/ui/magnetic-link"

API

Generated from the component's TypeScript types, so it always matches the source. * marks a required prop.

<MagneticLink>

PropTypeDefaultDescription
href*string—Link destination.
children*ReactNode—Content rendered inside the component.
classNamestring—Extra classes, merged with the component's own.

Source

src/components/ui/magnetic-link.tsx
"use client";

import * as React from "react";
import { ArrowUpRight } from "@phosphor-icons/react";
import { cn } from "@/lib/utils";

/** Link that drifts toward the cursor, with an arrow that nudges out. */
export function MagneticLink({
  href,
  children,
  className,
}: {
  href: string;
  children: React.ReactNode;
  className?: string;
}) {
  const ref = React.useRef<HTMLAnchorElement>(null);

  return (
    <a
      ref={ref}
      href={href}
      className={cn(
        "group inline-flex items-center gap-1.5 text-sm font-medium text-ink will-change-transform",
        className,
      )}
      onMouseMove={(e) => {
        const el = ref.current;
        if (!el) return;
        const r = el.getBoundingClientRect();
        el.style.transition = "transform 0.12s linear";
        el.style.transform = `translate(${((e.clientX - (r.left + r.width / 2)) / r.width) * 14}px, ${((e.clientY - (r.top + r.height / 2)) / r.height) * 10}px)`;
      }}
      onMouseLeave={() => {
        const el = ref.current;
        if (!el) return;
        el.style.transition = "transform 0.5s var(--ease-spring)";
        el.style.transform = "translate(0,0)";
      }}
    >
      <span className="border-b border-line-strong pb-px transition-colors duration-200 group-hover:border-accent group-hover:text-accent">
        {children}
      </span>
      <ArrowUpRight
        size={14}
        weight="bold"
        className="transition-transform duration-300 ease-out group-hover:-translate-y-0.5 group-hover:translate-x-0.5 group-hover:text-accent"
      />
    </a>
  );
}

Exports MagneticLink. Peer dependencies: clsx, tailwind-merge, @phosphor-icons/react.

PreviousMagnetic ButtonNextFooter Cta