# Widget Areas

> Place editor-managed content, menus, and built-in widgets in an Astro template.

Source: https://emdashcmseverything.com/official-docs/guides/widgets/

A widget area is a named position in a site template. Editors choose what appears there, while the template controls where the area sits and how its output is styled.

Use a widget area for content that should stay synchronized wherever the area appears, such as a sidebar, a footer column, or a promotional message. Use a [section](https://emdashcmseverything.com/official-docs/guides/sections/) when an editor should insert and then customize an independent copy inside an entry.

## Add a widget area

Create and fill widget areas in **Widgets** in the EmDash admin.

1. Click **Add Widget Area**. Enter the name the template will query, a label for the admin, and an optional description of where it appears.

2. Drag a widget from **Available Widgets** into the new area.

3. Configure the widget, then drag the widgets within the area to set their order.

The available widget types are:

- **Content** renders Portable Text entered by an editor.
- **Menu** renders a menu selected by name.
- **Component** renders one of the built-in components: recent posts, categories, tags, search, or archives.

The component settings in the admin control details such as item limits, dates, counts, and search placeholder text.

The following built-in component widgets are available:

| Component | What it renders |
| --------- | --------------- |
| `core:recent-posts` | Recent posts, with optional dates, thumbnails, and a link URL template (e.g. `/blog/:slug`) |
| `core:categories` | Category links and optional entry counts |
| `core:tags` | A limited list of tag links and optional counts |
| `core:search` | A search form that submits to `/search` |
| `core:archives` | Monthly or yearly post archive links |

The `core:recent-posts` link template accepts the same tokens as LiveSearch's `routeMap`: `:collection`, `:id` (the content ULID), `:slug` (the bare slug), and `:path` (slug or ID). Without a template, links keep their existing shape, including any Astro i18n locale prefix.

## Place the area in a template

Import `WidgetArea` from `emdash/ui`. The component fetches the named area, preserves the configured order, and renders nothing when the area is missing or empty.

The following layout places a sidebar area beside the page content:

```astro title="src/layouts/BlogPost.astro"
---
import { WidgetArea } from "emdash/ui";
---

<div class="page-with-sidebar">
  <main>
    <slot />
  </main>

  <aside aria-label="Sidebar">
    <WidgetArea name="sidebar" class="sidebar-widgets" />
  </aside>
</div>
```

`WidgetArea` adds `widget-area` and the supplied `class` to its wrapper. Each item uses the `widget`, `widget__title`, and `widget__content` classes. Content, menu, and built-in component widgets add more specific classes beneath them.

Astro component styles are scoped by default. Use a global style block when styling the markup rendered inside `WidgetArea`:

```astro title="src/layouts/BlogPost.astro"
<style is:global>
  .page-with-sidebar {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 18rem;
    gap: 2rem;
  }

  .sidebar-widgets {
    display: grid;
    gap: 1.5rem;
  }

  .sidebar-widgets .widget__title {
    margin-block-end: 0.75rem;
  }

  @media (max-width: 48rem) {
    .page-with-sidebar {
      grid-template-columns: 1fr;
    }
  }
</style>
```

## Menus and taxonomies in widgets

Menu, category, and tag widgets query data in the current request locale. Menu references also resolve to the translated content or term when one exists.

The built-in widget renderer uses the root-relative URL returned by the menu or taxonomy helper. It does not add Astro's locale prefix. On a multilingual site, render navigation and taxonomy lists with the [menu rendering pattern](https://emdashcmseverything.com/official-docs/guides/menus/#render-a-menu) or [taxonomy list pattern](https://emdashcmseverything.com/official-docs/guides/taxonomies/#query-a-term-list) when those links need locale prefixes. Content, search, recent-post, and archive widgets can still use the standard `WidgetArea` component.

> **Tip**
>
> A content widget is rendered as Portable Text. Use a component widget when the output needs to
> query content or apply custom Astro markup.

## Render an area yourself

Call `getWidgetArea()` when the site needs markup that the built-in renderer does not provide. The following example renders an area containing content and menu widgets, and adds Astro's locale prefix to menu links.

First, fetch the area and pass each configured widget to a renderer:

```astro title="src/components/LocalizedWidgetArea.astro"
---
import { getWidgetArea } from "emdash";
import LocalizedWidget from "./LocalizedWidget.astro";

interface Props {
  name: string;
}

const area = await getWidgetArea(Astro.props.name);
---

{area && area.widgets.length > 0 && (
  <div class="widget-area" data-widget-area={area.name}>
    {area.widgets.map((widget) => (
      <LocalizedWidget widget={widget} />
    ))}
  </div>
)}
```

Then handle content and menu widgets explicitly:

```astro title="src/components/LocalizedWidget.astro"
---
import { getMenu } from "emdash";
import type { Widget } from "emdash";
import { PortableText } from "emdash/ui";
import { getRelativeLocaleUrl } from "astro:i18n";

interface Props {
  widget: Widget;
}

const { widget } = Astro.props;
const locale = Astro.currentLocale;
const menu = widget.type === "menu" && widget.menuName
  ? await getMenu(widget.menuName, { locale })
  : null;

function menuHref(url: string) {
  return locale && url.startsWith("/")
    ? getRelativeLocaleUrl(locale, url)
    : url;
}
---

{widget.type !== "component" && (
  <section class="widget" data-widget-id={widget.id}>
    {widget.title && <h3>{widget.title}</h3>}

    {widget.type === "content" && widget.content && (
      <PortableText value={widget.content} />
    )}

    {widget.type === "menu" && menu && (
      <nav aria-label={widget.title}>
        <ul>
          {menu.items.map((item) => (
            <li>
              <a
                href={menuHref(item.url)}
                target={item.target}
                rel={item.target === "_blank" ? "noopener noreferrer" : undefined}
              >
                {item.label}
              </a>
            </li>
          ))}
        </ul>
      </nav>
    )}
  </section>
)}
```

This focused renderer produces no output for component widgets. Keep component widgets in the standard `WidgetArea`, or add explicit component-ID cases for the site components you support.

The [runtime API reference](https://emdashcmseverything.com/official-docs/reference/api/#widget-areas) documents `getWidgetArea()` and `getWidgetAreas()`. For programmatic changes, authenticate with a Bearer token and add `X-EmDash-Request: 1` to every state-changing request. See the [widget area endpoints](https://emdashcmseverything.com/official-docs/reference/rest-api/#widget-area-endpoints) for request bodies and responses.
