Skip to main content

Embed Mode Contract

Last Updated: December 2025
Status: Phase 3.x Implementation Complete

Overview

Embed mode allows the dashboard to be embedded in external CRM systems (Salesforce, HubSpot, etc.) via Canvas/iframe without showing the global sidebar and top navigation. Use Case: Salesforce/HubSpot Canvas-type integrations where we need a minimal UI that fits within the CRM’s layout.

Activation

Embed mode is activated via query parameter:
?mode=embedded
Example URLs:
  • https://dashboard.example.com/agent/robin-bundy?mode=embedded
  • https://dashboard.example.com/ceo?mode=embedded&period_label=2025-12-01

Behavior

When mode=embedded:

Preserved:
  • Authentication (JWT tokens, auth checks)
  • Routing (Next.js routing works normally)
  • Theme support (theme colors/styles still apply)
  • All page functionality (charts, tables, interactions)
Hidden:
  • Sidebar navigation (left sidebar)
  • Top header/navigation bar
  • Logo and branding elements

Layout Changes

Normal Mode:
┌─────────┬─────────────────────────────┐
│ Sidebar │ Header                      │
│         ├─────────────────────────────┤
│         │ Content Area                │
│         │                             │
└─────────┴─────────────────────────────┘
Embed Mode:
┌───────────────────────────────────────┐
│ Content Area (full width)             │
│                                       │
│                                       │
└───────────────────────────────────────┘

Implementation

Frontend Hook

import { useEmbedMode } from '@/hooks/useEmbedMode';

function MyComponent() {
  const { isEmbedded } = useEmbedMode();
  
  if (isEmbedded) {
    // Render embedded-specific UI
  }
  
  // Normal rendering
}

Layout Component

The DashboardLayout component automatically detects embed mode and conditionally renders sidebar/header:
// dashboard/src/components/layout/DashboardLayout.tsx
const { isEmbedded } = useEmbedMode();

if (isEmbedded) {
  // Render without sidebar/header
  return <main>{children}</main>;
}

// Normal layout with sidebar/header
return (
  <div>
    <Sidebar />
    <Header />
    <main>{children}</main>
  </div>
);

Security Considerations

Authentication: Embed mode does NOT bypass authentication. All auth checks remain in place. Authorization: Role-based access control (RBAC) still applies. Users can only access pages they have permission for. CSP/Frame Options: For production, consider configuring Content Security Policy (CSP) and X-Frame-Options headers to control which domains can embed the dashboard.

Testing

Manual Testing

  1. Normal Mode: Navigate to any page (e.g., /agent/robin-bundy)
    • ✅ Sidebar visible
    • ✅ Header visible
    • ✅ Content renders normally
  2. Embed Mode: Add ?mode=embedded (e.g., /agent/robin-bundy?mode=embedded)
    • ✅ Sidebar hidden
    • ✅ Header hidden
    • ✅ Content renders full-width
    • ✅ Auth still works
    • ✅ Routing still works

Automated Testing

// Example test
describe('Embed Mode', () => {
  it('hides sidebar and header when mode=embedded', () => {
    render(<DashboardLayout><TestContent /></DashboardLayout>, {
      router: { query: { mode: 'embedded' } }
    });
    
    expect(screen.queryByRole('complementary')).not.toBeInTheDocument(); // Sidebar
    expect(screen.queryByRole('banner')).not.toBeInTheDocument(); // Header
    expect(screen.getByText('TestContent')).toBeInTheDocument();
  });
});

Integration Examples

Salesforce Canvas

// Salesforce Canvas App Configuration
{
  "canvasAppUrl": "https://dashboard.example.com/agent/robin-bundy?mode=embedded",
  "height": "600px",
  "width": "100%"
}

HubSpot Custom App

<!-- HubSpot iframe embed -->
<iframe 
  src="https://dashboard.example.com/ceo?mode=embedded&period_label=2025-12-01"
  width="100%"
  height="800px"
  frameborder="0"
></iframe>

Future Enhancements (Phase 4)

  • Custom CSS: Allow CRM-specific styling via query params
  • PostMessage API: Bi-directional communication with parent CRM frame
  • Responsive Breakpoints: Optimize layout for different CRM frame sizes
  • Loading States: Show CRM-branded loading indicators

See Also:
  • dashboard/src/hooks/useEmbedMode.ts - Embed mode detection hook
  • dashboard/src/components/layout/DashboardLayout.tsx - Layout component with embed support
  • docs/INTEGRATION_READINESS.md - CRM integration readiness guide