Clipboard
The clipboard machine allows users to quickly copy content to clipboard.
Installation
To use the clipboard machine in your project, run the following command in your command line:
npm install @zag-js/clipboard @zag-js/react # or yarn add @zag-js/clipboard @zag-js/react
npm install @zag-js/clipboard @zag-js/solid # or yarn add @zag-js/clipboard @zag-js/solid
npm install @zag-js/clipboard @zag-js/vue # or yarn add @zag-js/clipboard @zag-js/vue
npm install @zag-js/clipboard @zag-js/vue # or yarn add @zag-js/clipboard @zag-js/vue
This command will install the framework agnostic clipboard logic and the reactive utilities for your framework of choice.
Anatomy
To set up the clipboard correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM`.
Usage
First, import the clipboard package into your project
import * as clipboard from "@zag-js/clipboard"
The clipboard package exports two key functions:
machine
— The state machine logic for the clipboard widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.
You'll also need to provide a unique
id
to theuseMachine
hook. This is used to ensure that every part has a unique identifier.
Next, import the required hooks and functions for your framework and use the clipboard machine in your project 🔥
import * as clipboard from "@zag-js/clipboard" import { useMachine, normalizeProps } from "@zag-js/react" import { ClipboardCheck, ClipboardCopyIcon } from "lucide-react" import { useId } from "react" function Clipboard() { const [state, send] = useMachine( clipboard.machine({ id: useId(), value: "https://github.com/chakra-ui/zag", }), ) const api = clipboard.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <label {...api.labelProps}>Copy this link</label> <div {...api.controlProps}> <input {...api.inputProps} /> <button {...api.triggerProps}> {api.isCopied ? <ClipboardCheck /> : <ClipboardCopyIcon />} </button> </div> </div> ) }
import * as clipboard from "@zag-js/clipboard" import { useMachine, normalizeProps } from "@zag-js/solid" import { ClipboardCheck, ClipboardCopyIcon } from "lucide-solid" import { createMemo, createUniqueId } from "solid-js" function Clipboard() { const [state, send] = useMachine( clipboard.machine({ id: createUniqueId(), value: "https://github.com/chakra-ui/zag", }), ) const api = createMemo(() => clipboard.connect(state, send, normalizeProps)) return ( <div {...api().rootProps}> <label {...api().labelProps}>Copy this link</label> <div {...api().controlProps}> <input {...api().inputProps} /> <button {...api().triggerProps}> <Show when={api().isCopied} fallback={<ClipboardCopyIcon />}> <ClipboardCheck /> </Show> </button> </div> </div> ) }
import * as clipboard from "@zag-js/clipboard" import { useMachine, normalizeProps } from "@zag-js/vue" import { ClipboardCheck, ClipboardCopyIcon } from "lucide-vue-next" import { computed, defineComponent } from "vue" export const Clipboard = defineComponent({ name: "Clipboard", setup() { const [state, send] = useMachine( clipboard.machine({ id: "1", value: "https://github.com/chakra-ui/zag", }), ) const apiRef = computed(() => clipboard.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <div {...api.rootProps}> <label {...api.labelProps}>Copy this link</label> <div {...api.controlProps}> <input {...api.inputProps} style={{ width: "100%" }} /> <button {...api.triggerProps}> {api.isCopied ? <ClipboardCheck /> : <ClipboardCopyIcon />} </button> </div> </div> ) } }, })
<script setup> import * as clipboard from "@zag-js/clipboard" import { useMachine, normalizeProps } from "@zag-js/vue" import { ClipboardCheck, ClipboardCopyIcon } from "lucide-vue-next" import { computed } from "vue" const [state, send] = useMachine( clipboard.machine({ id: "1", value: "https://github.com/chakra-ui/zag", }), ) const api = computed(() => clipboard.connect(state.value, send, normalizeProps), ) </script> <template> <div v-bind="api.rootProps"> <label v-bind="api.labelProps">Copy this link</label> <div v-bind="api.controlProps"> <input v-bind="api.inputProps" style="width: 100%" /> <button v-bind="api.triggerProps"> <ClipboardCheck v-if="api.isCopied" /> <ClipboardCopyIcon v-else /> </button> </div> </div> </template>
Setting the value to copy
You can set the value to copy by passing a value
prop to the machine
context.
const [state, send] = useMachine( clipboard.machine({ value: "Hello, world!", }), )
Listening to copy events
When the value is copied to the clipboard, the onStatusChange
event is fired.
You can listen to this event and perform any action you want.
const [state, send] = useMachine( clipboard.machine({ onStatusChange: (details) => { console.log("Copy status changed to", details.copied) }, }), )
Checking if the value is copied
Use the api.copied
property to check if the value is copied to the clipboard.
const api = clipboard.connect(state, send) if (api.copied) { console.log("Value is copied to the clipboard") }
Changing the timeout
By default, the clipboard machine will automatically reset the state to idle
after 3000ms
. You can change this timeout by passing a timeout
option to the
machine
context.
const [state, send] = useMachine( clipboard.machine({ timeout: 5000, }), )
Styling guide
Earlier, we mentioned that each clipboard part has a data-part
attribute added
to them to select and style them in the DOM.
[data-scope="clipboard"][data-part="root"] { /* styles for the root part */ }
Methods and Properties
Machine Context
The clipboard machine exposes the following context properties:
ids
Partial<{ root: string; input: string; label: string; }>
The ids of the elements in the clipboard. Useful for composition.value
string
The value to be copied to the clipboardonStatusChange
(details: CopyStatusDetails) => void
The function to be called when the value is copied to the clipboardtimeout
number
The timeout for the copy operationid
string
The unique identifier of the machine.getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The clipboard api
exposes the following methods:
copied
boolean
Whether the value has been copied to the clipboardvalue
string
The value to be copied to the clipboardsetValue
(value: string) => void
Set the value to be copied to the clipboardcopy
() => void
Copy the value to the clipboard
Edit this page on GitHub