Side Menu Component

Build slide-out panel components for quick-access tools

Side Menu Component

Side Menu components appear as slide-out panels from the side navigation. They're ideal for quick-access tools that users need frequently while working on other tasks.


When to Use

  • Quick actions that don't need a full page
  • Search or lookup tools
  • Notification panels
  • Utilities accessed from anywhere

Manifest Configuration

components:
  - type: side-menu
    title: "Quick Actions"
    id: "quick-actions-uuid"
    path: static/panel/build
    settings:
      icon: "lightning"
      width: "M"

Settings

SettingTypeRequiredValues
iconstringYesIcon identifier
widthstringYesS, M, or L

icon

The icon shown in the side navigation. Common options:

IconUse Case
LightningQuick actions
SearchSearch tool
BellNotifications
SettingsSettings
CalendarScheduling
HelpHelp/support
📘

See All Icons

Browse the complete icon library in the Design System Storybook.

width

The panel width when expanded:

ValueDescriptionUse Case
SSmallSimple actions, toggles
MMediumForms, lists
LLargeData tables, complex UI

Context Access

Side Menu components have access to user context only — not record context:

import FireberryClientSDK from "@fireberry/sdk/client";

const client = new FireberryClientSDK();
await client.initializeContext();

const { user } = client.context;

console.log(user.id); // "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
console.log(user.fullName); // "John Doe"

// record is NOT available in Side Menu components
// client.context.record will be empty
⚠️

Important

If your feature needs to know which record the user is viewing, use a Record Component instead.


Example: Quick Actions Panel

A panel with common actions:

import { useEffect, useState } from "react";
import FireberryClientSDK from "@fireberry/sdk/client";
import {
  DSThemeContextProvider,
  Typography,
  Button,
  List,
  ListItem,
  ListItemText,
} from "@fireberry/ds";

function QuickActionsPanel() {
  const [client] = useState(() => new FireberryClientSDK());
  const [recentItems, setRecentItems] = useState([]);

  useEffect(() => {
    async function init() {
      await client.initializeContext();

      // Fetch recent items
      const result = await client.api.query(1, {
        fields: "accountid,accountname",
        query: `ownername = "${client.context.user.fullName}"`,
        page_size: 5,
      });

      if (result.success) {
        const items = Array.isArray(result.data) ? result.data : [result.data];
        setRecentItems(items);
      }
    }

    init();
  }, [client]);

  const createNew = async () => {
    const result = await client.api.create(1, {
      name: `New Item - ${new Date().toLocaleDateString()}`,
      status: "draft",
    });

    if (result.success) {
      alert("Created!");
    }
  };

  return (
    <DSThemeContextProvider isRtl={false}>
      <div style={{ padding: 16 }}>
        <Typography type="title">Quick Actions</Typography>

        <div style={{ marginTop: 16 }}>
          <Button
            label="Create New"
            color="success"
            variant="primary"
            onClick={createNew}
          />
        </div>

        <Typography type="subTitle" style={{ marginTop: 24 }}>
          Recent Items
        </Typography>

        <List>
          {recentItems.map((item) => (
            <ListItem key={item.id}>
              <ListItemText>{item.name}</ListItemText>
            </ListItem>
          ))}
        </List>
      </div>
    </DSThemeContextProvider>
  );
}

Example: Search Tool

A search interface in a Side Menu:

import { useState, useEffect } from "react";
import FireberryClientSDK from "@fireberry/sdk/client";
import {
  DSThemeContextProvider,
  Typography,
  List,
  ListItem,
  ListItemText,
} from "@fireberry/ds";

function SearchPanel() {
  const [client] = useState(() => new FireberryClientSDK());
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [searching, setSearching] = useState(false);

  useEffect(() => {
    client.initializeContext();
  }, [client]);

  const search = async () => {
    if (!query.trim()) return;

    setSearching(true);
    const result = await client.api.query(1, {
      fields: "accountid,accountname,status",
      query: `accountname LIKE "${query}%"`,
      page_size: 10,
    });

    if (result.success) {
      const items = Array.isArray(result.data) ? result.data : [result.data];
      setResults(items);
    }
    setSearching(false);
  };

  return (
    <DSThemeContextProvider isRtl={false}>
      <div style={{ padding: 16 }}>
        <Typography type="title">Search</Typography>

        <div style={{ marginTop: 16, display: "flex", gap: 8 }}>
          <input
            type="text"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            onKeyPress={(e) => e.key === "Enter" && search()}
            placeholder="Search..."
            style={{
              flex: 1,
              padding: "8px 12px",
              border: "1px solid #E2E3E9",
              borderRadius: 4,
            }}
          />
          <button onClick={search}>Search</button>
        </div>

        {searching && <Typography type="caption">Searching...</Typography>}

        {results.length > 0 && (
          <List>
            {results.map((item) => (
              <ListItem key={item.id}>
                <ListItemText>{item.name}</ListItemText>
              </ListItem>
            ))}
          </List>
        )}
      </div>
    </DSThemeContextProvider>
  );
}

Width Guidelines

WidthBest For
Small (S)Action buttons, quick toggles, simple status
Medium (M)Search, short lists, forms
Large (L)Data tables, detailed lists, complex forms
💡

Tip

Start with Medium. Only go Large if your content genuinely needs the space.


Use Cases

Use CaseSuggested Width
Quick action buttonsS
Search toolM
Recent items listM
Notification feedM
Settings panelM or L
Data browserL

Validation Errors

ErrorSolution
icon is requiredAdd icon to settings
width must be S, M, or LUse S, M, or L

Next Steps