# App Frame With Sidebar
> A foundational layout component that provides the structure for an application, serving as an alternative to the standard header-centric app frame.

## Import

```js
import { AppFrameWithSidebar } from '@gemini-suite/vera-react';
```

App Frame With Sidebar is a compound component that consists of multiple parts to help you create different kinds of layout.

- `AppFrameWithSidebar.Root`: The outer container for other app frame elements. It renders children inside a CSS grid layout.
- `AppFrameWithSidebar.Header`: A wrapper for application or current view header.
- `AppFrameWithSidebar.HeaderSlot`: An empty space or slot that header content can “portal” into.
- `AppFrameWithSidebar.HeaderSlotPortal`: A wrapper for a piece of content that will be portaled into `HeaderSlot`.
- `AppFrameWithSidebar.Sidebar`: A wrapper for application sidebar that hosts primary navigation component.
- `AppFrameWithSidebar.Main`: A wrapper for main content.

## Examples

<Callout variant="tip">

Examples below include `maxHeight: 50vh` CSS rule, to ensure optimal docs-browsing experience.
It should not be added in a real app environment, as **App Frame With Sidebar** by design will fill up full viewport.

[See Storybook for fullscreen examples](https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/components-layout-appframewithsidebar--default)

</Callout>

### Basic

**App Frame With Sidebar** wraps an entire app view and helps split the viewport into key areas that houses primary sidebar navigation, header and main content of the app.
It implements [sidebar-based layout UX pattern](/UX-patterns/framework/app-layouts#sidebar-based-layout).

Unlike the [standard app frame](/components/app-frame) that features a prominent application header and typically a top, horizontal navigation bar, this sidebar-focused approach prioritizes vertical navigation hierarchy. The sidebar is designed to be a global, non-contextual element, that stays in place across different application views.

```jsx
<AppFrameWithSidebar.Root css={{ maxHeight: '50vh' }}>
  <AppFrameWithSidebar.Header>
    <Box
      padding="spacingM"
      css={{
        backgroundColor: '$backgroundNeutralMinimal',
        color: '$foregroundNeutral',
        borderBottom: '1px dashed $dataPurple80',
        height: '100%',
        display: 'flex',
        alignItems: 'center'
      }}
    >
      {'Header'}
    </Box>
  </AppFrameWithSidebar.Header>
  <AppFrameWithSidebar.Sidebar
    padding="spacingM"
    css={{ minWidth: '8rem', borderRight: '1px dashed $dataPurple80' }}
  >
    {'Sidebar'}
  </AppFrameWithSidebar.Sidebar>
  <AppFrameWithSidebar.Main padding="spacingM">
    {'Main content'}
  </AppFrameWithSidebar.Main>
</AppFrameWithSidebar.Root>
```

### Flexible layout

**App Frame With Sidebar** lets you pick and choose its parts to best fit your application requirements.
If you just need to define a global sidebar and main content for your layout, you can simply leave out rendering `AppFrameWithSidebar.Header`.

```jsx
<AppFrameWithSidebar.Root css={{ maxHeight: '50vh' }}>
  <AppFrameWithSidebar.Sidebar
    padding="spacingM"
    css={{ minWidth: '8rem', borderRight: '1px dashed $dataPurple80' }}
  >
    {'Sidebar'}
  </AppFrameWithSidebar.Sidebar>
  <AppFrameWithSidebar.Main padding="spacingM">
    {'Main content'}
  </AppFrameWithSidebar.Main>
</AppFrameWithSidebar.Root>
```

### With overflowing content

By default `AppFrameWithSidebar.Main` content will become scrollable as the content overflows.
`AppFrameWithSidebar.Header` and `AppFrameWithSidebar.Sidebar` are static while scrolling down the content.

<Callout variant="tip">

It is possible to set `scroll` prop to `body` on `AppFrameWithSidebar.Root`. This will make `AppFrameWithSidebar.Main` scrollable relative to the viewport.

</Callout>

```jsx
<AppFrameWithSidebar.Root css={{ maxHeight: '50vh' }}>
  <AppFrameWithSidebar.Header padding="spacingM">
    {'Header'}
  </AppFrameWithSidebar.Header>
  <AppFrameWithSidebar.Sidebar
    padding="spacingM"
    css={{ borderRight: '1px dashed $dataPurple80', minWidth: '8rem' }}
  >
    {'Sidebar'}
  </AppFrameWithSidebar.Sidebar>
  <AppFrameWithSidebar.Main padding="spacingM" background="neutralSubtle">
    <AutoGrid>
      {Array(6)
        .fill(null)
        .map((_, i) => (
          <Card.Root key={i}>
            <Card.Header as={Heading} variant="delta">
              {'Hobbitses regretted natural muster determined villager?'}
            </Card.Header>
            <Card.Body>
              <FakeText words="25" />
            </Card.Body>
          </Card.Root>
        ))}
    </AutoGrid>
  </AppFrameWithSidebar.Main>
</AppFrameWithSidebar.Root>
```

### With sidebar navigation

`AppFrameWithSidebar.Sidebar` should be used in combination with our [Sidebar Navigation component](/components/sidebar-navigation).

The example below is configured to display a sidebar navigation with a branding, app title, navigation items and a user menu.
Because all header elements are consolidated in the sidebar, standard app header at the top can be omitted.
This layout is especially effective for map-centric or full-canvas applications where you want to maximize the main view.

<Callout variant="tip">

[Visit Sidebar Navigation docs](/components/sidebar-navigation) for a more complete sidebar navigation experience examples and additional instructions.

[See example in Storybook](https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/demos-applayouts-sidebarbased--default)

</Callout>

```jsx
() => {
  const [userMenuContent, setUserMenuContent] = React.useState(null);

  return (
    <AppFrameWithSidebar.Root
      css={{ maxHeight: '50vh', isolation: 'isolate', position: 'relative' }}
    >
      <AppFrameWithSidebar.Sidebar>
        <SidebarNavigation.Root
          aria-label="Main navigation"
          mode="overlayContent"
          elevation="m"
          hoverArea={userMenuContent}
          css={{
            // the following override is added only to make the demo
            // work in the playground
            position: 'absolute'
          }}
        >
          <SidebarNavigation.Header>
            <Logo variant="icon" label="Gemini Suite logo" />
            <SidebarNavigation.AppTitle>
              {'Product name'}
            </SidebarNavigation.AppTitle>
          </SidebarNavigation.Header>
          <SidebarNavigation.Body>
            <SidebarNavigation.Items>
              <SidebarNavigation.Item iconName="home" label="Home" />
              <SidebarNavigation.Item iconName="layout" label="Dashboard" />
              <SidebarNavigation.Group iconName="cases" label="Group item">
                <SidebarNavigation.Item
                  iconName="download"
                  label="Sub item 1"
                />
                <SidebarNavigation.Item
                  iconName="download"
                  label="Sub item 2"
                />
              </SidebarNavigation.Group>
            </SidebarNavigation.Items>
          </SidebarNavigation.Body>
          <SidebarNavigation.Footer>
            <SidebarNavigation.Section title="Profile">
              <SidebarNavigation.Items>
                <SidebarNavigation.Item
                  iconName="settings"
                  label="Admin settings"
                />
                <Menu.Root>
                  <SidebarNavigation.Item
                    as={Menu.Trigger}
                    leadingVisual={
                      <Avatar.Root of="Ulf Sindre" size="small">
                        <Avatar.Initials />
                      </Avatar.Root>
                    }
                    label="Ulf Sindre"
                  />
                  <Menu.Content
                    placement={Placement.TOP_LEFT}
                    ref={setUserMenuContent}
                    css={{
                      maxWidth: `calc(${SIDEBAR_NAVIGATION_MEDIUM_WIDTH_EXPANDED} - 1rem)`
                    }}
                  >
                    <Flex gap="spacingM" padding="spacingS">
                      <Avatar.Root of="Ulf Sindre">
                        <Avatar.Initials />
                      </Avatar.Root>
                      <FlexItem shrinkPastContentSize>
                        {'Ulf Sindre'}
                        <Text
                          overflowStrategy="truncate"
                          as="div"
                          variant="zeta"
                          color="foregroundNeutralSubtle"
                        >
                          {'ulf.sindre@invera-tech.com'}
                        </Text>
                      </FlexItem>
                    </Flex>
                    <Menu.Separator />
                    <Menu.Item startElement={<SvgIcon iconName="user" />}>
                      {'Manage account'}
                    </Menu.Item>
                    <Menu.Separator />
                    <Menu.Item
                      intent="danger"
                      onSelect={() => {
                        alert('Log Out');
                      }}
                    >
                      {'Log Out'}
                    </Menu.Item>
                  </Menu.Content>
                </Menu.Root>
              </SidebarNavigation.Items>
            </SidebarNavigation.Section>
          </SidebarNavigation.Footer>
          <SidebarNavigation.Toggle label="Toggle panel" />
        </SidebarNavigation.Root>
      </AppFrameWithSidebar.Sidebar>
      <AppFrameWithSidebar.Main
        padding={{
          '@initial': 'spacingM',
          '@mqLargeAndUp': 'spacingL'
        }}
        background="neutralSubtle"
        css={{
          display: 'grid',
          placeItems: 'center'
        }}
      >
        <Text color="foregroundNeutralModerate">{'App canvas'}</Text>
      </AppFrameWithSidebar.Main>
    </AppFrameWithSidebar.Root>
  );
};
```

### With header

An optional `AppFrameWithSidebar.Header` can be added to the app frame to provide a more traditional layout.
This is useful for applications that require a more prominent header area for displaying additional information, status and/or actions.
This information may be contextual to the current view, such as the name of the view or a set of actions specific to it.

<Callout variant="tip">

[See example in Storybook](https://next--698f15fa99e7b146f9002f64.chromatic.com/?path=/story/demos-applayouts-sidebarbased--sidebar-and-header)

</Callout>

```jsx
() => {
  const [userMenuContent, setUserMenuContent] = React.useState(null);

  return (
    <AppFrameWithSidebar.Root css={{ maxHeight: '50vh', isolation: 'isolate' }}>
      <AppFrameWithSidebar.Header
        paddingY="spacingM"
        paddingX={{ '@initial': 'spacingM', '@mqLargeAndUp': 'spacingL' }}
      >
        <Heading variant="delta" margin="none" overflowStrategy="truncate">
          {'View title'}
        </Heading>
      </AppFrameWithSidebar.Header>
      <AppFrameWithSidebar.Sidebar>
        <SidebarNavigation.Root
          aria-label="Main navigation"
          withSeparator
          hoverArea={userMenuContent}
        >
          <SidebarNavigation.Header>
            <Logo variant="icon" label="Gemini Suite logo" />
            <SidebarNavigation.AppTitle>
              {'Product name'}
            </SidebarNavigation.AppTitle>
          </SidebarNavigation.Header>
          <SidebarNavigation.Body>
            <SidebarNavigation.Items>
              <SidebarNavigation.Item iconName="home" label="Home" />
              <SidebarNavigation.Item iconName="layout" label="Dashboard" />
              <SidebarNavigation.Group iconName="cases" label="Group item">
                <SidebarNavigation.Item
                  iconName="download"
                  label="Sub item 1"
                />
                <SidebarNavigation.Item
                  iconName="download"
                  label="Sub item 2"
                />
              </SidebarNavigation.Group>
            </SidebarNavigation.Items>
          </SidebarNavigation.Body>
          <SidebarNavigation.Footer>
            <SidebarNavigation.Section title="Profile">
              <SidebarNavigation.Items>
                <SidebarNavigation.Item
                  iconName="settings"
                  label="Admin settings"
                />
                <Menu.Root>
                  <SidebarNavigation.Item
                    as={Menu.Trigger}
                    leadingVisual={
                      <Avatar.Root of="Ulf Sindre" size="small">
                        <Avatar.Initials />
                      </Avatar.Root>
                    }
                    label="Ulf Sindre"
                  />
                  <Menu.Content
                    placement={Placement.TOP_LEFT}
                    ref={setUserMenuContent}
                    css={{
                      maxWidth: `calc(${SIDEBAR_NAVIGATION_MEDIUM_WIDTH_EXPANDED} - 1rem)`
                    }}
                  >
                    <Flex gap="spacingM" padding="spacingS">
                      <Avatar.Root of="Ulf Sindre">
                        <Avatar.Initials />
                      </Avatar.Root>
                      <FlexItem shrinkPastContentSize>
                        {'Ulf Sindre'}
                        <Text
                          overflowStrategy="truncate"
                          as="div"
                          variant="zeta"
                          color="foregroundNeutralSubtle"
                        >
                          {'ulf.sindre@invera-tech.com'}
                        </Text>
                      </FlexItem>
                    </Flex>
                    <Menu.Separator />
                    <Menu.Item startElement={<SvgIcon iconName="user" />}>
                      {'Manage account'}
                    </Menu.Item>
                    <Menu.Separator />
                    <Menu.Item
                      intent="danger"
                      onSelect={() => {
                        alert('Log Out');
                      }}
                    >
                      {'Log Out'}
                    </Menu.Item>
                  </Menu.Content>
                </Menu.Root>
              </SidebarNavigation.Items>
            </SidebarNavigation.Section>
          </SidebarNavigation.Footer>
          <SidebarNavigation.Toggle label="Toggle panel" />
        </SidebarNavigation.Root>
      </AppFrameWithSidebar.Sidebar>
      <AppFrameWithSidebar.Main
        padding={{
          '@initial': 'spacingM',
          '@mqLargeAndUp': 'spacingL'
        }}
        css={{
          display: 'grid',
          placeItems: 'center'
        }}
      >
        <Text color="foregroundNeutralModerate">{'App view'}</Text>
      </AppFrameWithSidebar.Main>
    </AppFrameWithSidebar.Root>
  );
};
```

### Header portal

Most web apps have persistent root layout with header and navigation sections that are shared across multiple pages/views.
Whenever a route changes to any component or view that is within the layout, its state is preserved because the layout component is not unmounted.
However, while moving between routes inside the same layout, you may want to contextually change the header contents, such as the name of the view or set of actions specific to it.

**App Frame With Sidebar** exposes `AppFrameWithSidebar.HeaderSlot` and `AppFrameWithSidebar.HeaderSlotPortal` that allow you to define app header content wherever your app requires it and render it inside header area using [portal technique](https://react.dev/reference/react-dom/createPortal).

```jsx
<React.Fragment>
  {/* AppFrame used in an app-wide root layout component */}
  <AppFrameWithSidebar.Root css={{ maxHeight: '50vh' }}>
    <AppFrameWithSidebar.Header
      paddingY="spacingM"
      paddingX={{ '@initial': 'spacingM', '@mqLargeAndUp': 'spacingL' }}
    >
      <AppFrameWithSidebar.HeaderSlot as={Flex} main="space-between" />
    </AppFrameWithSidebar.Header>
    <AppFrameWithSidebar.Sidebar
      padding="spacingM"
      css={{ borderRight: '1px dashed $dataPurple80', minWidth: '8rem' }}
    >
      {'Sidebar'}
    </AppFrameWithSidebar.Sidebar>
    <AppFrameWithSidebar.Main
      padding={{ '@initial': 'spacingM', '@mqLargeAndUp': 'spacingL' }}
      css={{
        display: 'grid',
        placeItems: 'center'
      }}
    >
      {/* The contents below could be defined in a separate component attached to an app route */}
      <AppFrameWithSidebar.HeaderSlotPortal>
        <FlexItem as={Flex} shrinkPastContentSize>
          <SvgIcon iconName="attachment" size="medium" />
          <Heading variant="delta" margin="none" overflowStrategy="truncate">
            {'View title'}
          </Heading>
        </FlexItem>
        <FlexItem shrink={0} marginY="-spacingXs">
          <Button leftIcon={<SvgIcon iconName="add" />}>{'Action'}</Button>
        </FlexItem>
      </AppFrameWithSidebar.HeaderSlotPortal>
      <Text color="foregroundNeutralModerate">{'App view'}</Text>
    </AppFrameWithSidebar.Main>
  </AppFrameWithSidebar.Root>
</React.Fragment>
```

---

## Accessibility

By default, `AppFrameWithSidebar.Main` and `AppFrameWithSidebar.Header` render semantic HTML tags to mark regions in the app and provide
[WAI-ARIA landmark roles](https://www.w3.org/TR/wai-aria-1.2/#landmark_roles).

The `AppFrameWithSidebar.Sidebar` component does not provide a default landmark role, as its content may define its own role.
When using `SidebarNavigation` inside `AppFrameWithSidebar.Sidebar` an appropriate landmark role will be included with the sidebar component.

---

## API Reference

### AppFrameWithSidebar.Root

| Name     | Type               | Default  | Description                                                                                                                                                                                                                                  | Required |
| -------- | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `css`    | `StitchesCss`      |          | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries or variants. |          |
| `scroll` | `"main" \| "body"` | `"main"` | When `main` scrolling will be contained within `AppFrameWithSidebar.Main`, `body` keeps the default body scrolling behavior.                                                                                                                 |          |

### AppFrameWithSidebar.Header

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name       | Type                                                      | Default    | Description                                                                                                                                                                                                                                                                                  | Required |
| ---------- | --------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`       | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `header`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`      | `StitchesCss`                                             |            | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
| `isSticky` | `boolean`                                                 |            | When `true`, the header will stick to the top of the screen while scrolling. Use in combination with setting `scroll` to `body` on `AppFrame`.                                                                                                                                               |          |
| `size`     | `"small" \| "medium" \| "auto"`                           | `"medium"` | Size affects the minimum height of the header. When `auto`, no minimum height will be applied to the header, making it always follow the content height.                                                                                                                                     |          |

### AppFrameWithSidebar.HeaderSlot

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name  | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ----- | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |

### AppFrameWithSidebar.HeaderSlotPortal

| Name       | Type              | Default | Description | Required |
| ---------- | ----------------- | ------- | ----------- | -------- |
| `children` | `React.ReactNode` |         |             |          |

### AppFrameWithSidebar.Sidebar

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name  | Type                                                      | Default | Description                                                                                                                                                                                                                                                                                  | Required |
| ----- | --------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`  | `keyof JSX.IntrinsicElements \| React.ComponentType<any>` | `div`   | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css` | `StitchesCss`                                             |         | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |

### AppFrameWithSidebar.Main

<Callout variant="tip">

In addition to the props below, you can pass [Box props](/components/layout/box#api-reference) to control the spacing.

</Callout>

| Name         | Type                                                       | Default            | Description                                                                                                                                                                                                                                                                                  | Required |
| ------------ | ---------------------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `as`         | `keyof JSX.IntrinsicElements \| React.ComponentType<any>`  | `main`             | Change the component to a different HTML tag or custom component. This will merge the original component props with the props of the supplied element/component and change the underlying DOM node.   For more details, read our [Composition](/get-started/composition#polymorphism) guide. |          |
| `css`        | `StitchesCss`                                              |                    | Apply styles directly to a component in a similar way how you would define inline styles. Vera uses [Stitches](https://stitches.dev/) under the hood with a fully-typed API and support for features like tokens, media queries, or variants.                                                |          |
| `background` | `"neutralMinimal" \| "neutralSubtle" \| "neutralModerate"` | `"neutralMinimal"` | The background of the main area. Subset of background values from [color tokens](/tokens/colors) are available.                                                                                                                                                                              |          |
