UploadKit
SDK@uploadkitdev/react

UploadButtonMagnetic

A pill-shaped upload button that attracts toward the cursor with a spring.

A soft, pill-shaped upload button that leans toward the cursor using a Motion spring — the classic "magnetic" interaction popularized by Apple's product pages. Without motion installed, it falls back to a CSS hover scale so the component still feels alive.

Design inspiration: Apple product pages (iPhone, Mac), Awwwards-tier portfolio sites.

motion is an optional peerDependency. Without it, the magnetic pull is disabled and the button uses a CSS hover scale instead. pnpm add motion to enable the spring-based cursor attraction.

Apple — cursor-attracted spring scale

Basic usage

import { UploadButtonMagnetic } from '@uploadkitdev/react';

export default function Page() {
  return (
    <UploadButtonMagnetic
      route="avatarUploader"
      accept={['image/jpeg', 'image/png']}
      maxSize={2 * 1024 * 1024}
      onUploadComplete={(result) => {
        console.log('Avatar URL:', result.url);
      }}
    >
      Upload avatar
    </UploadButtonMagnetic>
  );
}

Props

PropTypeDefaultDescription
routestringRequired. Route name from your file router.
acceptstring[]Accepted MIME types for the hidden file input.
maxSizenumberMaximum file size in bytes. Client-side UX validation only.
metadataRecord<string, unknown>JSON-serializable metadata forwarded to the server.
onUploadComplete(result: UploadResult) => voidCalled after a successful upload.
onUploadError(error: Error) => voidCalled when an upload fails.
disabledbooleanfalseDisables the button.
classNamestringAdditional CSS class(es) merged onto the <button> element.
childrenReactNodeCustom label. Defaults to "Upload file"; during upload becomes "Uploading {progress}%".

UploadButtonMagnetic accepts a ref forwarded to the underlying <button> element (either a plain <button> or Motion's motion.button, depending on whether motion is installed).

Theming

UploadButtonMagnetic reads these CSS custom properties:

VariablePurpose
--uk-bg-secondaryButton background (intentionally subtle — the motion carries the emphasis)
--uk-textLabel color
--uk-borderPill border color
--uk-fontFont family
--uk-primaryfocus-visible outline color

The pill radius is fixed at 999px and the shadow on hover is 0 14px 40px -16px rgba(0, 0, 0, 0.35).

Accessibility

  • aria-busy is set to true during upload
  • aria-label updates to "Uploading {progress}%" while uploading
  • The hidden file <input> is aria-hidden="true" and tabIndex={-1}
  • focus-visible outline uses --uk-primary at 2px offset
  • Respects prefers-reduced-motion — the magnetic spring, hover scale, and shadow transitions all collapse to near-zero duration

See the Theming guide for token overrides and dark mode.

On this page