,

Detecting and Controlling Sidebars in the Block Editor

hanging brown wooden frame

Sidebars (technically called “complementary areas” in Gutenberg’s architecture) are the panels that appear on the right side of the editor interface. Understanding how to detect which sidebar is open and how to control them programmatically is essential for creating advanced editor customizations.

All examples in this article are available at https://github.com/juanma-wp/sidebar-detecting-controlling-examples repo

Table of Contents

  1. Detecting Which Sidebar is Open
    1. The getActiveComplementaryArea Selector
    2. Understanding Scopes
    3. Possible Return Values
    4. Practical Example: Conditional Rendering
  2. Opening and Closing Sidebars Programmatically
    1. Direct API: enableComplementaryArea
    2. Store-Specific Actions: openGeneralSidebar
  3. Advanced Use Cases
    1. Smart Sidebar Opening Based on Context
    2. Creating Custom Plugin Sidebars
  4. Opening Specific Panels Within Sidebars
    1. Using toggleEditorPanelOpened
    2. Common Panel Names
    3. Ensuring a Panel is Open (Not Toggled)
    4. Complete Example: Opening a Sidebar and Panel
    5. Inserter Sidebar
  5. Key Takeaways
  6. Related WordPress Documentation

Detecting Which Sidebar is Open

The getActiveComplementaryArea Selector

The primary way to detect which sidebar is currently active is through the getActiveComplementaryArea selector from the core/interface store (store value imported from @wordpress/interface:

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import { __ } from '@wordpress/i18n';
import { cog } from '@wordpress/icons';
import { store as interfaceStore } from '@wordpress/interface';
import { useSelect } from '@wordpress/data';

/**
 * Sidebar content component
 */
const SidebarContent = () => {
    // Get the active state using the interface store
    
    const activeSidebar = useSelect((select) => {
    return select(interfaceStore).getActiveComplementaryArea("core");
    }, []);


    if (activeSidebar == null) {
      console.log("Sidebar is inactive");
    } else {
      console.log(`Sidebar is active: ${activeSidebar}`);
    }
    return null;
};

// Register the main plugin with sidebar
registerPlugin('sidebar-log', {
    render: SidebarContent,
    icon: cog,
});

By using useSelect we subscribe to any change in the getActiveComplementaryArea("core") value so every time the status sidebar change (is opened/closed for example) the component reacts

Console Command (run from /wp-admin/post.php?post=X&action=edit):

// Get currently active sidebar
wp.data.select('core/interface').getActiveComplementaryArea('core')
// Returns: 'edit-post/document', 'edit-post/block', null, etc.

Understanding Scopes

The getActiveComplementaryArea function requires a scope parameter that identifies which editor context you’re querying:

  • 'core' – The main unified scope for both post editor and site editor (most common)
  • 'core/edit-widgets' – Scope for the widgets editor
  • Custom plugin scopes – Plugins can define their own scopes like 'myplugin/custom-screen-a'

Note: The scopes 'core/edit-post' and 'core/edit-site' are deprecated and automatically normalized to 'core' for backward compatibility.

Possible Return Values

The function returns the identifier of the currently active complementary area, or null/undefined if no sidebar is open:

Built-in Post Editor Sidebars:

  • 'edit-post/document' – The document settings sidebar (post title, status, categories, etc.)
  • 'edit-post/block' – The block inspector sidebar (settings for the selected block)

Site Editor Sidebars:

  • 'edit-site/global-styles' – The global styles sidebar

Widgets Editor Sidebars:

  • 'edit-widgets/block-areas' – The widget areas sidebar
  • 'edit-widgets/block-inspector' – The block inspector in the widgets editor

Plugin Sidebars:

  • Custom identifiers like 'my-plugin/insert-image-sidebar'

No Active Sidebar:

  • null or undefined when no complementary area is active

Practical Example: Conditional Rendering

You can use this selector to conditionally render components based on the active sidebar:

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import { __ } from '@wordpress/i18n';
import { cog } from '@wordpress/icons';
import { store as interfaceStore } from '@wordpress/interface';
import { useSelect } from '@wordpress/data';

/**
 * Sidebar content component
 */
const SidebarContent = () => {
    // Get the active state using the interface store
    
    const { activeSidebar, isDocumentSidebarOpen } = useSelect((select) => {
        const activeSidebar = select(interfaceStore).getActiveComplementaryArea("core");
        const isDocumentSidebarOpen = activeSidebar === "edit-post/document";
        return { activeSidebar, isDocumentSidebarOpen };
    }, []);


    if (isDocumentSidebarOpen) {
      console.log(`The document sidebar is open: ${activeSidebar}`);
    }
    return null;
};

// Register the main plugin with sidebar
registerPlugin('sidebar-log', {
    render: SidebarContent,
    icon: cog,
});

Console Commands (run from /wp-admin/post.php?post=X&action=edit):

// Check if document sidebar is open
wp.data.select('core/interface').getActiveComplementaryArea('core') === 'edit-post/document'

// Check if block inspector is open
wp.data.select('core/interface').getActiveComplementaryArea('core') === 'edit-post/block'

// Check if any sidebar is open
!!wp.data.select('core/interface').getActiveComplementaryArea('core')

Opening and Closing Sidebars Programmatically

There are several ways to open sidebars programmatically in the Block Editor:

1. Direct API: enableComplementaryArea & disableComplementaryArea

The most direct method is using the enableComplementaryArea action from the core/interface store:

/**
 * WordPress dependencies
 */
import { registerPlugin } from '@wordpress/plugins';
import { __ } from '@wordpress/i18n';
import { cog } from '@wordpress/icons';
import { store as interfaceStore } from '@wordpress/interface';
import { useSelect, useDispatch } from '@wordpress/data';
import { useCommand } from "@wordpress/commands";

const SidebarCommands = () => {
  const { enableComplementaryArea, disableComplementaryArea } =
    useDispatch(interfaceStore);

  const isDocumentSidebarOpen = useSelect((select) => {
    const activeSidebar =
      select(interfaceStore).getActiveComplementaryArea("core");
    return activeSidebar === "edit-post/document";
  }, []);

  // Register open command when sidebar is closed
  useCommand({
    name: "sidebar-log/open-document-sidebar",
    label: __("Open Document Sidebar (custom)", "sidebar-log"),
    icon: cog,
    callback: () => {
      enableComplementaryArea("core", "edit-post/document");
    },
    disabled: isDocumentSidebarOpen, // Disabled when sidebar is open
  });

  // Register close command when sidebar is open
  useCommand({
    name: "sidebar-log/close-document-sidebar",
    label: __("Close Document Sidebar (custom)", "sidebar-log"),
    icon: cog,
    callback: () => disableComplementaryArea("core"),
    disabled: !isDocumentSidebarOpen, // Disabled when sidebar is closed
  });

  return null;
};


// Register the plugin with sidebar commands
registerPlugin('sidebar-log', {
    render: SidebarCommands,
    icon: cog,
});

Console Commands (run from /wp-admin/post.php?post=X&action=edit):

// Open document sidebar (post settings)
wp.data.dispatch('core/interface').enableComplementaryArea('core', 'edit-post/document')

// Open block inspector
wp.data.dispatch('core/interface').enableComplementaryArea('core', 'edit-post/block')

2. Store-Specific Actions: openGeneralSidebar & closeGeneralSidebar

Each editor store provides convenience wrapper actions:

In the Post Editor (@wordpress/edit-post):

import { store as interfaceStore } from "@wordpress/interface";

import { useSelect, useDispatch } from "@wordpress/data";
import { useCommandLoader } from "@wordpress/commands";
import { __ } from "@wordpress/i18n";
import { useMemo } from "@wordpress/element";

const getSidebarCommandsAlt2 = () => function useSidebarCommandsAlt2({ search }) {
  const { openGeneralSidebar, closeGeneralSidebar } =
    useDispatch("core/edit-post");

  const isDocumentSidebarOpen = useSelect((select) => {
    const activeSidebar =
      select(interfaceStore).getActiveComplementaryArea("core");
    return activeSidebar === "edit-post/document";
  }, []);

  const commands = useMemo(() => {
    const baseCommands = [];

    if (!isDocumentSidebarOpen) {
      baseCommands.push({
        name: "sidebar-log/open-document-sidebar-alt",
        label: __("Open Document Sidebar (custom alt2)", "sidebar-log"),
        callback: () => openGeneralSidebar("edit-post/document"),
      });
    } else {
      baseCommands.push({
        name: "sidebar-log/close-document-sidebar-alt",
        label: __("Close Document Sidebar (custom alt2)", "sidebar-log"),
        callback: () => closeGeneralSidebar("edit-post/document"),
      });
    }

    return baseCommands;
  }, [isDocumentSidebarOpen]);

  return { commands, isLoading: false };
};

const OpenCloseDocumentSidebarAlt2 = () => {
  useCommandLoader({
    name: "sidebar-log/commands-alt2",
    hook: getSidebarCommandsAlt2(),
  });

  return null;
};

export default OpenCloseDocumentSidebarAlt2;

We could also run the following commands from the JS console of the browser while in the post editor

// Alternative: Using edit-post wrapper
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/document')
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/block')

For the Site Editor (@wordpress/edit-site) we would do

const { openGeneralSidebar, closeGeneralSidebar } = useDispatch( 'core/edit-site' );
...
openGeneralSidebar('edit-site/global-styles'),

Or run the following commands from the JS console of the browser while in the site editor

// Open global styles sidebar
wp.data.dispatch('core/edit-site').openGeneralSidebar('edit-site/global-styles')

// Open template sidebar
wp.data.dispatch('core/edit-site').openGeneralSidebar('edit-site/template')

Note: These wrapper actions internally call enableComplementaryArea on the interface store.

Advanced Use Cases

Smart Sidebar Opening Based on Context

The following code registers a command that opens the right sidebar depending on the context:

import { useSelect, useDispatch } from "@wordpress/data";
import { useCommand } from "@wordpress/commands";
import { __ } from "@wordpress/i18n";
import { sidebar } from "@wordpress/icons";

const SmartSidebar = () => {
  const { activeSidebar, hasBlockSelection } = useSelect((select) => {
    const { getActiveComplementaryArea } = select("core/interface");
    const { getBlockSelectionStart } = select("core/block-editor");

    return {
      activeSidebar: getActiveComplementaryArea("core"),
      hasBlockSelection: !!getBlockSelectionStart(),
    };
  }, []);

  const { enableComplementaryArea, disableComplementaryArea } =
    useDispatch("core/interface");

  const isAnySidebarOpen = ["edit-post/document", "edit-post/block"].includes(
    activeSidebar
  );

  useCommand({
    name: "sidebar-log/smart-sidebar-toggle",
    label: __(
      isAnySidebarOpen ? "Close (smart) Sidebar" : "Open (smart) Sidebar",
      "sidebar-log"
    ),
    icon: sidebar,
    callback: () => {
      if (isAnySidebarOpen) {
        disableComplementaryArea("core");
      } else {
        const sidebarToOpen = hasBlockSelection
          ? "edit-post/block"
          : "edit-post/document";
        enableComplementaryArea("core", sidebarToOpen);
      }
    },
  });

  return null;
};

export default SmartSidebar;

From the post editor we could also run the following commands from the JS console of the browser

// Smart toggle (like Ctrl+Shift+,) - opens block inspector if block selected, otherwise document sidebar
const activeSidebar = wp.data.select('core/interface').getActiveComplementaryArea('core');
const hasBlockSelection = !!wp.data.select('core/block-editor').getBlockSelectionStart();
const isAnySidebarOpen = ['edit-post/document', 'edit-post/block'].includes(activeSidebar);

if (isAnySidebarOpen) {
    wp.data.dispatch('core/interface').disableComplementaryArea('core');
} else {
    const sidebarToOpen = hasBlockSelection ? 'edit-post/block' : 'edit-post/document';
    wp.data.dispatch('core/interface').enableComplementaryArea('core', sidebarToOpen);
}

Creating Custom Plugin Sidebars

When creating your own plugin sidebar, you’ll register it with a custom identifier using registerPlugin. You can then detect and control this sidebar using its full identifier:

import { registerPlugin } from '@wordpress/plugins';
import { PluginSidebar, PluginSidebarMoreMenuItem } from '@wordpress/editor';
import { PanelBody } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useCommand } from '@wordpress/commands';
import { __ } from '@wordpress/i18n';
import {image} from '@wordpress/icons';

const CustomPluginSidebar = () => {
  const isOpen = useSelect((select) => {
    return (
      select('core/interface').getActiveComplementaryArea('core') ===
      'complementary-area-toggle-demo/custom-sidebar'
    );
  }, []);

  const { enableComplementaryArea, disableComplementaryArea } =
    useDispatch('core/interface');

  useCommand({
    name: 'my-plugin-sidebar/toggle',
    label: __(
      isOpen ? 'Close Custom Sidebar' : 'Open Custom Sidebar',
      'sidebar-log'
    ),
      icon: image,
    callback: () => {
      if (isOpen) {
        disableComplementaryArea('core');
      } else {
        enableComplementaryArea('core', 'complementary-area-toggle-demo/custom-sidebar');
      }
    },
  });

  return (
    <>
      <PluginSidebarMoreMenuItem target="custom-sidebar">
        My Custom Sidebar
      </PluginSidebarMoreMenuItem>
      <PluginSidebar
        name="custom-sidebar"
        icon={image}
        title="My Custom Sidebar"
      >
        <PanelBody>
          <p>Custom sidebar content</p>
        </PanelBody>
      </PluginSidebar>
    </>
  );
};

registerPlugin("complementary-area-toggle-demo", {
  render: CustomPluginSidebar,
});

Opening Specific Panels Within Sidebars

Once a sidebar is open, you may want to expand or collapse specific panels (sections) within that sidebar. The Block Editor provides dedicated actions for this purpose.

Using toggleEditorPanelOpened

The core/edit-post store provides the toggleEditorPanelOpened action to control individual panels:

import { useDispatch } from '@wordpress/data';

function PanelControls() {
    const { openGeneralSidebar, toggleEditorPanelOpened } = useDispatch( 'core/edit-post' );

    const openStatusPanel = () => {
        // First ensure the document sidebar is open
        openGeneralSidebar( 'edit-post/document' );

        // Then toggle the specific panel
        toggleEditorPanelOpened( 'post-status' );
    };

    return (
        <button onClick={ openStatusPanel }>
            Open Post Status Panel
        </button>
    );
}

From the post editor we could also run the following commands from the JS console of the browser

// Get all available panels and their state
wp.data.select('core/edit-post').getPreferences()

// Check if specific panel is open (returns true/false regardless of sidebar visibility)
wp.data.select('core/edit-post').isEditorPanelOpened('post-status')

// Toggle a panel
wp.data.dispatch('core/edit-post').toggleEditorPanelOpened('post-status')

Common Panel Names

Post Editor Document Sidebar Panels:

  • 'post-status' – Status & visibility settings
  • 'taxonomy-panel-category' – Categories
  • 'taxonomy-panel-post_tag' – Tags
  • 'post-excerpt' – Post excerpt field
  • 'discussion-panel' – Allow comments and pingbacks
  • 'featured-image' – Featured image selector (if theme supports it)
  • 'page-attributes' – Template, parent, and order settings

Available panels depend on your post type, theme support, and installed plugins. Use wp.data.select('core/edit-post').getPreferences() to see all available panels in your setup.

Custom plugin panels registered via PluginDocumentSettingPanel use a custom identifier.

Ensuring a Panel is Open (Not Toggled)

Since toggleEditorPanelOpened toggles the panel state, you may want to check if it’s already open before toggling:

const { openGeneralSidebar, toggleEditorPanelOpened } = useDispatch( 'core/edit-post' );
const isPanelOpen = useSelect( ( select ) => {
        return select( 'core/edit-post' ).isEditorPanelOpened( 'post-status' );
    }, [] );

const ensurePanelOpen = () => {
    // Open the document sidebar first
    openGeneralSidebar( 'edit-post/document' );

    // Only toggle if the panel is not already open
    if ( ! isPanelOpen ) {
        toggleEditorPanelOpened( 'post-status' );
    }
};

From the post editor you could also run the following commands from the JS console of the browser

// Open sidebar and ensure panel is expanded
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/document');
if (!wp.data.select('core/edit-post').isEditorPanelOpened('post-status')) {
    wp.data.dispatch('core/edit-post').toggleEditorPanelOpened('post-status');
}
// Check if panel is actually visible (sidebar open AND panel expanded)
const sidebarOpen = wp.data.select('core/interface').getActiveComplementaryArea('core') === 'edit-post/document';
const panelExpanded = wp.data.select('core/edit-post').isEditorPanelOpened('post-status');
console.log('Panel visible:', sidebarOpen && panelExpanded);

isEditorPanelOpened() returns whether a panel is expanded or collapsed, not whether it’s visible. A panel can be “opened” (expanded) even when the sidebar is closed. To check actual visibility, verify both the sidebar state and panel state:

Complete Example: Opening a Sidebar and Panel

Here’s a complete example that opens a sidebar and ensures a specific panel is expanded:

/**
 * WordPress dependencies
 */
import { useDispatch, useSelect } from '@wordpress/data';
import { useCommand } from '@wordpress/commands';
import { __ } from '@wordpress/i18n';
import { category } from '@wordpress/icons';

/**
 * Component to open a specific panel inside a sidebar
 */
const OpenSpecificPanel = () => {
	const { openGeneralSidebar, toggleEditorPanelOpened } = useDispatch( 'core/edit-post' );

	const isCategoriesPanelOpen = useSelect( ( select ) => {
		return select( 'core/edit-post' ).isEditorPanelOpened( 'taxonomy-panel-category' );
	}, [] );

	// Dynamic command that changes based on panel state
	useCommand( {
		name: 'complementary-area-toggle-demo/toggle-categories-panel',
		label: __(
			isCategoriesPanelOpen ? 'Close (specific) Categories Panel' : 'Open (specific) Categories Panel',
			'complementary-area-toggle-demo'
		),
		icon: category,
		callback: () => {
			if ( ! isCategoriesPanelOpen ) {
				// First ensure the document sidebar is open
				openGeneralSidebar( 'edit-post/document' );
			}

			// Then toggle the specific panel
			toggleEditorPanelOpened( 'taxonomy-panel-category' );
		},
	} );

	return null;
};

Inserter Sidebar

For the Inserter Sidebar we can use the setIsInserterOpened dispatcher:

/**
 * WordPress dependencies
 */
import { useDispatch, useSelect } from '@wordpress/data';
import { useCommand } from '@wordpress/commands';
import { __ } from '@wordpress/i18n';
import { plus } from '@wordpress/icons';

/**
 * Component to toggle the block inserter
 */
const ToggleInserter = () => {
	const { setIsInserterOpened } = useDispatch( 'core/edit-post' );

	const isInserterOpen = useSelect( ( select ) => {
		return select( 'core/edit-post' ).isInserterOpened();
	}, [] );

	useCommand( {
		name: 'complementary-area-toggle-demo/toggle-inserter',
		label: __(
			isInserterOpen ? 'Close Block Inserter' : 'Open Block Inserter',
			'complementary-area-toggle-demo'
		),
		icon: plus,
		callback: () => {
			setIsInserterOpened( ! isInserterOpen );
		},
	} );

	return null;
};

export default ToggleInserter;

To select a specific tab we can pass an object to setIsInserterOpened

setIsInserterOpened({tab: "media"})

From the site editor we can run from the JS Console in the browser:

// Check if inserter is open
wp.data.select('core/edit-site').isInserterOpened()

// Open/close inserter
wp.data.dispatch('core/edit-site').setIsInserterOpened(true)
wp.data.dispatch('core/edit-site').setIsInserterOpened(false)

// Toggle inserter
wp.data.dispatch('core/edit-site').setIsInserterOpened(!wp.data.select('core/edit-site').isInserterOpened())

// Close inserter and open template settings
wp.data.dispatch('core/edit-site').setIsInserterOpened(false);
wp.data.dispatch('core/edit-site').openGeneralSidebar('edit-site/template');

From the post editor we can also do:

wp.data.dispatch('core/editor').setIsInserterOpened({tab: "patterns"})

Key Takeaways

  1. Use getActiveComplementaryArea( 'core' ) to detect which sidebar is currently open
  2. Use enableComplementaryArea( 'core', identifier ) to open a specific sidebar
  3. Use disableComplementaryArea( 'core' ) to close any open sidebar
  4. Use toggleEditorPanelOpened( panelName ) to expand/collapse panels within sidebars
  5. Use isEditorPanelOpened( panelName ) to check if a panel is currently expanded (not visible)
  6. Panel expanded state persists even when sidebar is closed – check both conditions for actual visibility
  7. Sidebar identifiers follow the pattern editor-name/sidebar-name (e.g., edit-post/document)
  8. The scope parameter ('core') determines which editor context the sidebar belongs to
  9. All methods ultimately call the core/interface store actions for sidebars
  10. Always open the sidebar first before attempting to control panels within it
  11. Test commands in browser console using wp.data.select() and wp.data.dispatch() for quick debugging

Understanding these patterns allows you to create sophisticated editor experiences that respond to and control both the editor’s sidebar state and the individual panels within those sidebars effectively.

👨‍💻 See the gist with the code that can be run from the JS console

// ============================================
// DETECTING SIDEBAR STATE
// ============================================
// Get currently active sidebar
wp.data.select('core/interface').getActiveComplementaryArea('core')
// Returns: 'edit-post/document', 'edit-post/block', null, etc.
// Check if document sidebar is open
wp.data.select('core/interface').getActiveComplementaryArea('core') === 'edit-post/document'
// Check if block inspector is open
wp.data.select('core/interface').getActiveComplementaryArea('core') === 'edit-post/block'
// Check if any sidebar is open
!!wp.data.select('core/interface').getActiveComplementaryArea('core')
// Check if specific panel is open
wp.data.select('core/edit-post').isEditorPanelOpened('taxonomy-panel-category')
// Check panels available
wp.data.select('core/edit-post').getPreferences()
// ============================================
// OPENING SIDEBARS
// ============================================
// Open document sidebar (post settings)
wp.data.dispatch('core/interface').enableComplementaryArea('core', 'edit-post/document')
// Open block inspector
wp.data.dispatch('core/interface').enableComplementaryArea('core', 'edit-post/block')
// Alternative: Using edit-post wrapper
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/document')
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/block')
// ============================================
// CLOSING SIDEBARS
// ============================================
// Close any open sidebar
wp.data.dispatch('core/interface').disableComplementaryArea('core')
// Alternative: Using edit-post wrapper
wp.data.dispatch('core/edit-post').closeGeneralSidebar()
// ============================================
// TOGGLE SIDEBARS
// ============================================
// Toggle document sidebar
const current = wp.data.select('core/interface').getActiveComplementaryArea('core');
if (current === 'edit-post/document') {
wp.data.dispatch('core/interface').disableComplementaryArea('core');
} else {
wp.data.dispatch('core/interface').enableComplementaryArea('core', 'edit-post/document');
}
// Smart toggle (like Ctrl+Shift+,)
const activeSidebar = wp.data.select('core/interface').getActiveComplementaryArea('core');
const hasBlockSelection = !!wp.data.select('core/block-editor').getBlockSelectionStart();
const isAnySidebarOpen = ['edit-post/document', 'edit-post/block'].includes(activeSidebar);
if (isAnySidebarOpen) {
wp.data.dispatch('core/interface').disableComplementaryArea('core');
} else {
const sidebarToOpen = hasBlockSelection ? 'edit-post/block' : 'edit-post/document';
wp.data.dispatch('core/interface').enableComplementaryArea('core', sidebarToOpen);
}
// ============================================
// CONTROLLING PANELS WITHIN SIDEBARS
// ============================================
// Toggle a panel (post-status, featured-image, etc.)
wp.data.dispatch('core/edit-post').toggleEditorPanelOpened('taxonomy-panel-category')
// Open sidebar and ensure panel is expanded
wp.data.dispatch('core/edit-post').openGeneralSidebar('edit-post/document');
if (!wp.data.select('core/edit-post').isEditorPanelOpened('taxonomy-panel-category')) {
wp.data.dispatch('core/edit-post').toggleEditorPanelOpened('taxonomy-panel-category');
}
// Get panels available
// Common panel names: 'post-status', 'taxonomy-panel-category'
wp.data.select('core/edit-post').getPreferences()
view raw wp-sidebars.js hosted with ❤ by GitHub

Leave a Reply

Navigation

About

Writing on the Wall is a newsletter for freelance writers seeking inspiration, advice, and support on their creative journey.

Discover more from JuanMa Codes

Subscribe now to keep reading and get access to the full archive.

Continue reading