Overview
The useBlockSelected hook determines whether a specific block is currently selected (active) in the editor. This is useful for highlighting, showing block-specific controls, or conditional rendering.
Usage
import { useBlockSelected } from '@yoopta/editor';
function BlockWrapper({ blockId, children }) {
const isSelected = useBlockSelected({ blockId });
return (
<div className={isSelected ? 'block-selected' : 'block'}>
{children}
{isSelected && <button>Delete</button>}
</div>
);
}
Signature
function useBlockSelected(
props: { blockId: string; at?: number } | { at: number; blockId?: string }
): boolean
Parameters
You must provide either blockId or at (or both):
The unique identifier of the block to check. If provided without at, this will be used to find the block.
The path index (order) of the block to check. If provided without blockId, this will be used to find the block.
Returns
boolean - true if the block is currently selected, false otherwise
Error Handling
The hook will throw an error if neither blockId nor at is provided:
// ❌ Error: useBlockSelected must receive either blockId or at
useBlockSelected({});
Examples
Selection by Block ID
function BlockControls({ blockId }: { blockId: string }) {
const isSelected = useBlockSelected({ blockId });
if (!isSelected) return null;
return (
<div className="block-controls">
<button>Move Up</button>
<button>Move Down</button>
<button>Delete</button>
</div>
);
}
Selection by Path Index
function BlockByIndex({ index }: { index: number }) {
const isSelected = useBlockSelected({ at: index });
return (
<div style={{
backgroundColor: isSelected ? '#e3f2fd' : 'transparent'
}}>
Block at position {index}
</div>
);
}
Highlight Selected Block
function HighlightableBlock({ blockId, children }) {
const isSelected = useBlockSelected({ blockId });
return (
<div
style={{
border: isSelected ? '2px solid #2196f3' : '2px solid transparent',
borderRadius: '4px',
padding: '8px',
transition: 'border-color 0.2s',
}}
>
{children}
</div>
);
}
Show/Hide Block Actions
function BlockWithActions({ blockId, children }) {
const isSelected = useBlockSelected({ blockId });
const editor = useYooptaEditor();
const handleDelete = () => {
editor.deleteBlock({ blockId });
};
const handleDuplicate = () => {
editor.duplicateBlock({ blockId });
};
return (
<div className="block-container">
{children}
{isSelected && (
<div className="block-actions">
<button onClick={handleDuplicate}>Duplicate</button>
<button onClick={handleDelete}>Delete</button>
</div>
)}
</div>
);
}
Selection Indicator
function SelectionIndicator({ blockId }: { blockId: string }) {
const isSelected = useBlockSelected({ blockId });
return (
<div className="selection-indicator">
{isSelected ? (
<span style={{ color: '#2196f3' }}>✓ Selected</span>
) : (
<span style={{ color: '#999' }}>○ Not selected</span>
)}
</div>
);
}
Drag Handle Visibility
function DragHandle({ blockId }: { blockId: string }) {
const isSelected = useBlockSelected({ blockId });
const [isHovered, setIsHovered] = useState(false);
const showHandle = isSelected || isHovered;
return (
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{showHandle && (
<button className="drag-handle" draggable>
⋮⋮
</button>
)}
</div>
);
}
Combined with Block Data
function SmartBlockWrapper({ blockId, children }) {
const isSelected = useBlockSelected({ blockId });
const block = useBlockData(blockId);
if (!block) return null;
const isHeading = block.type.startsWith('Heading');
const showOutline = isSelected && isHeading;
return (
<div
style={{
outline: showOutline ? '2px dashed #ff9800' : 'none',
padding: showOutline ? '8px' : '0',
}}
>
{children}
{isSelected && (
<div className="block-info">
Type: {block.type} | Depth: {block.meta.depth}
</div>
)}
</div>
);
}
Use Cases
- Visual Feedback: Highlight the active block with borders or backgrounds
- Contextual Controls: Show block-specific actions only when selected
- Drag Handles: Display drag handles for block reordering
- Selection State UI: Show selection indicators in block lists
- Keyboard Navigation: Style blocks differently during keyboard navigation
- Block-specific Toolbars: Show formatting options for selected blocks
- Analytics: Track which blocks users interact with most
How Selection Works
The hook compares the current editor path (editor.path.current) with the block’s order:
// Simplified implementation
const useBlockSelected = ({ blockId, at }) => {
const editor = useYooptaEditor();
let block;
if (blockId) {
block = editor.children[blockId];
}
if (at !== undefined) {
block = Blocks.getBlock(editor, { at });
}
return editor.path.current === block?.meta.order;
};
This hook will re-render whenever the editor’s selection changes. For components that render many blocks:
- Use
React.memo to prevent unnecessary re-renders
- Consider virtualization for long documents
- Memoize heavy computations based on selection state
const BlockWrapper = React.memo(({ blockId, children }) => {
const isSelected = useBlockSelected({ blockId });
return (
<div className={isSelected ? 'selected' : ''}>
{children}
</div>
);
});
See Also