A registered widget with no block equivalent renders inside a legacy widget block, which keeps it working and keeps it visible as unfinished.
// hide a widget from the legacy block's picker, so
// editors cannot add new instances of something deprecated
add_filter( 'widget_types_to_hide_from_legacy_widget_block',
function ( array $types ): array {
$types[] = 'turkerdev_old_promo';
return $types;
}
);
// existing instances keep rendering. only the picker changes.
Hiding from the picker while keeping existing instances working is the right shape for a deprecation: nothing breaks and nothing new is created. It also gives a countable measure of progress, since the number of remaining instances is a database query. The legacy block calls the widget’s own render method, so anything that worked before works now — including a widget that echoed instead of returning, which is most of them.