2026年11月12日

2026年11月12日

WordPress Widgets Not Showing or Saving: Causes and Fixes

This article is a localized edition of the Japanese original. Read the Japanese original

WordPress widget problems are easier to track down once you sort them into one of four cases: "the Widgets screen is missing", "changes will not save", "widgets disappeared after a theme change" or "a custom widget throws an error". This article explains the causes and fixes for each symptom in WordPress as of October 2026. It also shows how to export your widget settings before you move widgets around or delete settings.

Symptoms and main causes

Symptom Main cause Section
There is no "Widgets" item under "Appearance" You are using a block theme If the Widgets screen is missing
Changes will not save, or an error appears when saving REST API blocked, plugin conflict, blocked by a WAF If changes will not save
Widgets disappeared after switching themes The widget area IDs do not match the new theme If widgets disappeared after a theme change
Widgets appear in the admin but not on the site The template does not output them, or the IDs do not match If widgets do not show on the site
Fatal error from a custom widget The class is defined twice, or method declarations do not match the parent class Custom widget errors

Check the widget state before you start

If WP-CLI is available, check the current state first.

# List registered widget areas (sidebars)
wp sidebar list --fields=name,id

# Widgets in a specific area
wp widget list sidebar-1 --fields=name,id,position

# Widgets not placed in any area (inactive)
wp widget list wp_inactive_widgets --fields=name,id

Widget placement is stored in the sidebars_widgets option, and each widget's settings are stored in options starting with widget_, such as widget_block (block widgets) and widget_text (Text widgets).

Take a backup

Before you move or delete anything, export the widget settings. A full database backup (wp db export) works too, but exporting only the widgets makes restoring easier. Save the following as export-widgets.php and run it in the WordPress directory.

<?php
// Usage: wp eval-file export-widgets.php widgets-backup.json
global $wpdb;

$file = $args[0] ?? 'widgets-backup.json';

$names = $wpdb->get_col(
	$wpdb->prepare(
		"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
		$wpdb->esc_like( 'widget_' ) . '%'
	)
);

$data = array(
	'sidebars_widgets' => get_option( 'sidebars_widgets' ),
	'widgets'          => array(),
);
foreach ( $names as $name ) {
	$data['widgets'][ $name ] = get_option( $name );
}

file_put_contents( $file, wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ) );
WP_CLI::success( sprintf( 'Exported %d widget settings to %s', count( $names ), $file ) );
wp eval-file export-widgets.php widgets-backup.json

To restore, use the following import-widgets.php.

<?php
// Usage: wp eval-file import-widgets.php widgets-backup.json
$file = $args[0] ?? 'widgets-backup.json';

$data = json_decode( (string) file_get_contents( $file ), true );
if ( ! is_array( $data ) || ! isset( $data['sidebars_widgets'] ) ) {
	WP_CLI::error( "Could not read {$file}" );
}

foreach ( $data['widgets'] as $name => $value ) {
	update_option( $name, $value );
}
update_option( 'sidebars_widgets', $data['sidebars_widgets'] );

WP_CLI::success( 'Restored. Check the layout under Appearance > Widgets' );
wp eval-file import-widgets.php widgets-backup.json

This restore assumes the same theme (the same widget area IDs) as when you exported. Moving widgets to a different theme is covered in a later section.

If the Widgets screen is missing

If "Widgets" does not appear under the "Appearance" (外観 in the Japanese UI) menu, you are most likely using a block theme (a theme edited in the Site Editor, such as Twenty Twenty-Five). Block themes have no widget areas; you edit the contents of sidebars and footers as template parts and blocks in the Site Editor.

If you are using a classic theme (such as Twenty Twenty-One) and the screen still does not appear, the theme does not register a widget area with register_sidebar(). See "Register a widget area" below.

If changes will not save

Since WordPress 5.8, the Widgets screen of classic themes uses the block editor (the block widget editor), and saving goes through the REST API (/wp-json/wp/v2/widgets and so on). If changes will not save, check the following in order.

  1. Open the Network tab of your browser's developer tools, save, and see whether the requests to /wp-json/wp/v2/ return 403 or 500.
  2. For 403, a security plugin's REST API restriction or a server WAF (such as ModSecurity) may be blocking the request. A WAF can block saving widgets that contain HTML or scripts. Check the WAF log on your hosting.
  3. For 500, find the plugin responsible in wp-content/debug.log (if WP_DEBUG_LOG is enabled) or the PHP error log.
  4. If you still cannot find the cause, deactivate all plugins temporarily and try saving (wp plugin deactivate --all does this, but first note the output of wp plugin list --status=active --field=name so you can restore them).

To go back to the classic Widgets screen, activate the official Classic Widgets plugin or use the following filter.

<?php
// Disable the block widget editor and return to the classic Widgets screen
add_filter( 'use_widgets_block_editor', '__return_false' );

The classic screen cannot edit widgets that use blocks, so if you rely mainly on blocks, prioritize fixing the underlying cause.

If widgets disappeared after a theme change

When you switch themes, WordPress uses the widget area IDs and similar clues to carry the layout over to the new theme. Widgets for which no matching area is found are not deleted; they move to "Inactive widgets".

In the admin, you can move them from "Inactive widgets" under "Appearance" → "Widgets" into an area of the new theme. With WP-CLI:

# Check inactive widgets
wp widget list wp_inactive_widgets --fields=name,id

# Move to an area of the new theme (e.g. sidebar-1). --position sets the order
wp widget move text-2 --sidebar-id=sidebar-1
wp widget move block-5 --sidebar-id=sidebar-1 --position=1

If you build themes, keeping widget area IDs consistent across your themes makes it more likely that layouts carry over when users switch.

If widgets do not show on the site

Register a widget area

Register widget areas with register_sidebar() on the widgets_init hook. Use a lowercase ASCII string for id that is unique within the site.

<?php
add_action(
	'widgets_init',
	function () {
		register_sidebar(
			array(
				'name'          => 'Main sidebar',
				'id'            => 'sidebar-main',
				'description'   => 'Sidebar for posts and pages',
				'before_widget' => '<section id="%1$s" class="widget %2$s">',
				'after_widget'  => '</section>',
				'before_title'  => '<h2 class="widget-title">',
				'after_title'   => '</h2>',
			)
		);
	}
);

Output it in a template

Registering an area does not display it on the site. Call dynamic_sidebar() in a theme template (such as sidebar.php), using the same ID as in register_sidebar().

<?php if ( is_active_sidebar( 'sidebar-main' ) ) : ?>
	<aside id="secondary" class="widget-area">
		<?php dynamic_sidebar( 'sidebar-main' ); ?>
	</aside>
<?php endif; ?>

is_active_sidebar() returns false when the area contains no widgets. If widgets are placed in the admin but do not show, check with the template and WP-CLI's wp sidebar list that the IDs match.

Custom widget errors

“Cannot declare class” and “Cannot redeclare”

Fatal error: Cannot declare class My_Custom_Widget, because the name is already in use is the error you get when a class with the same name is defined twice (for functions it is Cannot redeclare). Typical causes are putting the same code in both functions.php and a plugin, loading a file twice, or clashing with another plugin's name. Give class names your own prefix and wrap them in class_exists() to prevent it.

“must be compatible with WP_Widget::widget()”

When you extend WP_Widget, adding types such as array to the parameters of widget(), form() or update() conflicts with the parent class's declarations (which have no types) and causes a fatal error.

PHP Fatal error: Declaration of My_Custom_Widget::widget(array $args, array $instance): void must be compatible with WP_Widget::widget($args, $instance)

Match the parent class and leave the parameters untyped. The example below is a widget that shows a title and body text. Put it in a plugin (or a file in wp-content/mu-plugins/).

<?php
/**
 * Plugin Name: My Custom Widget
 */

if ( ! class_exists( 'My_Custom_Widget' ) ) {
	class My_Custom_Widget extends WP_Widget {

		public function __construct() {
			parent::__construct(
				'my_custom_widget',
				'Custom widget',
				array( 'description' => 'A widget that shows a title and body text' )
			);
		}

		// Front-end output (no parameter types, to match the parent class)
		public function widget( $args, $instance ) {
			$title   = apply_filters( 'widget_title', $instance['title'] ?? '', $instance, $this->id_base );
			$content = $instance['content'] ?? '';

			echo $args['before_widget'];
			if ( '' !== $title ) {
				echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
			}
			echo '<div class="widget-content">' . wp_kses_post( wpautop( $content ) ) . '</div>';
			echo $args['after_widget'];
		}

		// Admin form fields
		public function form( $instance ) {
			$title   = $instance['title'] ?? '';
			$content = $instance['content'] ?? '';
			?>
			<p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Title:</label>
				<input class="widefat" type="text"
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
					value="<?php echo esc_attr( $title ); ?>">
			</p>
			<p>
				<label for="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>">Body:</label>
				<textarea class="widefat" rows="5"
					id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>"
					name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>"><?php echo esc_textarea( $content ); ?></textarea>
			</p>
			<?php
		}

		// Sanitize on save
		public function update( $new_instance, $old_instance ) {
			return array(
				'title'   => sanitize_text_field( $new_instance['title'] ?? '' ),
				'content' => wp_kses_post( $new_instance['content'] ?? '' ),
			);
		}
	}
}

add_action(
	'widgets_init',
	function () {
		register_widget( 'My_Custom_Widget' );
	}
);

In the block widget editor, you add custom widgets with the "Legacy Widget" block.

Hiding a widget with widget_display_callback

The widget_display_callback filter receives a widget's settings (an array) right before it is displayed. Returning false stops that widget from being displayed. That is why declaring an anonymous function's return type as : array and then returning false stops with a type error. Leave out the return type or use array|false.

<?php
// Hide the widget with ID my_custom_widget-2 from visitors who are not logged in
add_filter(
	'widget_display_callback',
	function ( $instance, $widget, $args ) {
		if ( 'my_custom_widget-2' === $widget->id && ! is_user_logged_in() ) {
			return false;
		}
		return $instance;
	},
	10,
	3
);

What widget_types_to_hide_from_legacy_widget_block means

widget_types_to_hide_from_legacy_widget_block is a filter that specifies which widget types to hide from the choices in the "Legacy Widget" block (added in WordPress 5.8). It is an exclusion list, not an allowlist, and by default it contains standard widgets that have equivalent blocks, such as Text, Calendar and Recent Posts. If you add your custom widget to it, it can no longer be added from the block widget editor, so be careful.

Summary

  • A missing "Widgets" item under "Appearance" usually means you are using a block theme. Edit in the Site Editor instead
  • Check the state with wp sidebar list and wp widget list. Placement lives in sidebars_widgets, settings in options starting with widget_
  • Export widget settings before deleting or restoring. Do not delete sidebars_widgets on a whim
  • If changes will not save, check the status of the REST API requests and suspect blocking by a WAF or plugin
  • Widgets that disappeared after a theme change are in "Inactive widgets". wp widget move can move them to a new area
  • Do not add types to the parameters of widget(), form() and update() in custom widgets. Prefix class names and wrap them in class_exists()

For the basics of using widgets, see the basics of WordPress widget settings; if the sidebar disappeared after a theme update, see what to do when the sidebar disappears after an update.

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲