Getting Started
Build interactive text with custom markup - quick start in minutes.
Installation
Section titled “Installation”Install Markput using your preferred package manager:
npm install rc-marked-inputpnpm add rc-marked-inputyarn add rc-marked-inputbun add rc-marked-inputRequirements: React 17 or later.
Your First Editor
Section titled “Your First Editor”Let’s build a marked text editor with autocomplete in three steps.
Step 1: Render Marks
Section titled “Step 1: Render Marks”Here’s a basic editor rendering marked text. Try clicking the highlighted text:
import {MarkedInput} from 'rc-marked-input'
export const Step1Demo = () => ( <MarkedInput Mark={({value, meta}) => <mark onClick={() => alert(meta)}>{value}</mark>} defaultValue="Hello @[World](123)!" />)How it works:
Markput uses a special markup syntax to represent interactive elements as plain text:
- Markup — a text pattern that encodes structured data:
@[__value__](__meta__) - Value — the visible text shown to users (e.g.,
World) - Meta — hidden metadata for your app (e.g.,
123- user ID) - Mark — a React component that renders the markup visually
When Markput encounters @[World](123) in the text:
- Parses the markup and extracts:
value="World",meta="123" - Renders your
Markcomponent with these props:<mark onClick={...}>{props.value}</mark> - Preserves the original text as a simple string — easy to save or send to any backend
Since Mark is a regular React component, you can style it, add click handlers (like the onClick that shows an alert), or use any React features.
Step 2: Add Autocomplete
Section titled “Step 2: Add Autocomplete”Add the options prop to enable autocomplete suggestions:
import {MarkedInput} from 'rc-marked-input'
export const Step2Demo = () => ( <MarkedInput defaultValue="Type @ to mention someone!" options={[ { markup: '__value__', slotProps: { overlay: { trigger: '@', data: ['Alice', 'Bob', 'Charlie', 'Diana', 'Eve'], }, }, }, ]} />)How it works:
The options prop configures autocomplete behavior:
markup— the pattern for inserted text (__value__inserts plain text)slotProps.overlay— configuration for the built-inSuggestionscomponent:trigger— character that opens the overlay (here:@)data— array of suggestions to display
When you type the trigger character @:
- Markput detects the trigger and shows the built-in
Suggestionscomponent - As you type, suggestions are filtered based on your input
- When you select an item (e.g.,
'Alice'), Markput inserts the text
Keyboard navigation is built-in (↑↓ to navigate, Enter to select, Esc to close).
Step 3: Custom Overlay
Section titled “Step 3: Custom Overlay”The built-in Suggestions component is convenient, but sometimes you need full control over the UI. The Overlay prop lets you render a completely custom component, and the useOverlay hook provides all the state and actions you need.
Here’s a custom mention UI that fetches GitHub users and displays avatars:
import {MarkedInput, useOverlay} from 'rc-marked-input'import {useState, useEffect, type RefObject} from 'react'
const User = ({avatar, login, onClick}: {avatar?: string; login?: string; onClick?: () => void}) => ( <span className={`inline-flex gap-2 ${onClick ? '!flex p-2 hover:bg-gray-100' : ''}`} onClick={onClick}> <img src={avatar} alt="" className="rounded-full w-6 h-6" /> {login} </span>)
const CustomOverlay = () => { const {select, match, style, ref} = useOverlay() const [users, setUsers] = useState<{login: string; avatar_url: string}[]>([])
useEffect(() => { if (!match.value) return fetch(`https://api.github.com/search/users?q=${match.value}`) .then(res => res.json()) .then(data => setUsers(data.items?.slice(0, 10) || [])) }, [match.value])
useEffect(() => ref.current?.showPopover(), [])
return ( <div ref={ref as RefObject<HTMLDivElement>} popover="auto" style={{top: style.top, left: style.left}} className="border border-gray-200 shadow-md" > {users?.map(user => ( <User key={user.login} avatar={user.avatar_url} login={user.login} onClick={() => select({value: user.login, meta: user.avatar_url})} /> ))} </div> )}
export const Step3Demo = () => ( <MarkedInput defaultValue="Type @ to mention someone!" Mark={({value, meta}) => <User avatar={meta} login={value} />} Overlay={CustomOverlay} />)How it works:
The useOverlay hook returns an object with everything you need to build a custom overlay:
match— current search state withmatch.valuecontaining what the user typed after the triggerselect— function to insert markup:select({value: string, meta?: string})close— function to dismiss the overlay without selectingstyle— object withtopandleftcoordinates for positioning near the cursorref— React ref to attach to your overlay element for proper event handling
The hook handles all the complexity: detecting triggers, tracking the search query, and positioning the overlay. When you call select({value, meta}), it inserts the markup and closes the overlay.
In this example, we fetch GitHub users and store the username as value and avatar URL as meta. The Mark component then uses meta to display the avatar. You can use any UI library or custom component. Markput doesn’t impose styling constraints.
Try It Live
Section titled “Try It Live”Explore these interactive examples on CodeSandbox:
- Static Marks — Basic example with clickable marks
- Configured Component — Using
createMarkedInputfactory - Dynamic Marks — Editable and removable marks with
useMark - Custom Overlay — Building your own suggestion UI