Inline row actions

A per-row ⋮ menu with edit, duplicate, and delete. Destructive actions show a confirmation modal before committing. State is updated optimistically — no network call needed to see the result.

Name
Department
Role
Aria Chen
Engineering
Engineering Lead
Marcus Webb
Product
Product Manager
Priya Kapoor
Design
Senior Designer
Jordan Ellis
Analytics
Data Scientist
Sam Rivera
Engineering
DevOps Engineer
Taylor Brooks
Sales
Account Manager

The actions column

Use button: true on the column so clicks don't bubble to onRowClicked, and keep the width tight so it doesn't crowd other columns:

{
  name: '',
  button: true,
  width: '48px',
  cell: row => <RowMenu row={row} onEdit={openEdit} onDelete={openDelete} />,
}

Closing the dropdown on outside click

Track open state per-row inside the menu component and close on a document mousedown that falls outside the ref:

function RowMenu({ row, onEdit, onDelete }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const handle = e => {
      if (ref.current && !ref.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', handle);
    return () => document.removeEventListener('mousedown', handle);
  }, [open]);

  return (
    <div ref={ref}>
      <button onClick={e => { e.stopPropagation(); setOpen(o => !o); }}></button>
      {open && (
        <div className="dropdown">
          <button onClick={() => { setOpen(false); onEdit(row); }}>Edit</button>
          <button onClick={() => { setOpen(false); onDelete(row); }}>Delete</button>
        </div>
      )}
    </div>
  );
}

Confirmation before delete

Keep a modal state at the table level. The menu sets it; the modal reads it and calls the commit function on confirm:

type Modal = { type: 'none' } | { type: 'delete'; row: Employee };

const [modal, setModal] = useState<Modal>({ type: 'none' });

function commitDelete(row: Employee) {
  setData(prev => prev.filter(r => r.id !== row.id));
  setModal({ type: 'none' });
}

{modal.type === 'delete' && (
  <ConfirmDialog
    message={`Delete ${modal.row.name}?`}
    onConfirm={() => commitDelete(modal.row)}
    onCancel={() => setModal({ type: 'none' })}
  />
)}