When using Daily Prebuilt (iframe mode), you can customize its appearance through themes, CSS injection, layout configuration, custom sidebar integrations, and custom tray buttons.
Theme and CSS customization apply to Daily Prebuilt (iframe-based calls). Call Object mode gives you full control over UI and does not use these APIs.
Themes
DailyThemeConfig
A theme is either a single set of colors (applied regardless of the user’s OS preference) or separate light and dark variants:
type DailyThemeConfig =
| { colors: DailyThemeColors } // single theme
| { light: DailyTheme; dark: DailyTheme }; // adaptive theme
type DailyTheme = { colors: DailyThemeColors };
DailyThemeColors
All color values are optional hex strings (e.g. '#3B82F6'):
Primary action color. Used for buttons, active states, and keyboard focus rings.
Text color rendered on top of accent backgrounds.
Main background color of the call UI.
Background color for highlighted or elevated elements (e.g. toolbars).
Default text color, as rendered on background or backgroundAccent.
Default border color for bordered elements.
Background color of the main video call area.
Background color for individual video tiles in the main area.
Text color for text rendered inside the main call area (e.g. participant names).
Text color for secondary, less-emphasized text.
setTheme() and theme()
// Apply a theme
const applied = await call.setTheme({
colors: {
accent: '#6366F1',
accentText: '#FFFFFF',
background: '#0F172A',
backgroundAccent: '#1E293B',
baseText: '#F1F5F9',
border: '#334155',
mainAreaBg: '#0F172A',
mainAreaBgAccent: '#1E293B',
mainAreaText: '#F1F5F9',
supportiveText: '#94A3B8',
},
});
// Read the current theme
const current = call.theme();
Setting a theme at construction time
Pass theme directly to createFrame() or wrap() to apply it before the call loads:
const call = Daily.createFrame({
theme: {
light: {
colors: {
accent: '#3B82F6',
background: '#FFFFFF',
mainAreaBg: '#F8FAFC',
},
},
dark: {
colors: {
accent: '#60A5FA',
background: '#0F172A',
mainAreaBg: '#1E293B',
},
},
},
});
theme-updated event
call.on('theme-updated', ({ theme }) => {
console.log('Theme changed:', theme);
});
Custom CSS
Inject CSS into Prebuilt for styling beyond what the theme colors allow.
At construction time
const call = Daily.createFrame({
bodyClass: 'my-call-theme',
cssFile: 'https://example.com/daily-overrides.css',
cssText: `
.daily-video-tile { border-radius: 12px; }
.daily-controls { background: transparent; }
`,
});
After construction (iframe mode only)
call.loadCss({
bodyClass: 'updated-theme',
cssFile: 'https://example.com/updated.css',
cssText: '.my-class { color: red; }',
});
CSS class name(s) added to the <body> element inside the Prebuilt iframe.
URL of an external stylesheet to load inside the iframe.
Inline CSS string injected as a <style> tag inside the iframe.
Layout configuration
Control grid tile counts via DailyLayoutConfig:
const call = Daily.createFrame({
layoutConfig: {
grid: {
maxTilesPerPage: 9,
minTilesPerPage: 1,
},
},
});
Custom integrations
Embed external web apps as either a sidebar panel or a main area overlay inside Prebuilt.
Defining integrations
call.setCustomIntegrations({
'my-app': {
label: 'My App',
location: 'sidebar', // 'sidebar' | 'main'
src: 'https://app.example.com/embed',
iconURL: 'https://app.example.com/icon.png',
controlledBy: 'owners', // '*' | 'owners' | string[]
shared: true, // sync state with all participants
allow: 'camera; microphone',
sandbox: 'allow-scripts allow-same-origin',
loading: 'lazy',
},
});
// Read currently registered integrations
const integrations = call.customIntegrations();
Display name shown in Prebuilt’s UI.
location
'main' | 'sidebar'
required
Where the integration iframe is rendered.
Source URL for the embedded iframe.
Inline HTML content for the iframe (alternative to src).
controlledBy
'*' | 'owners' | string[]
Who can start and stop this integration. Default: '*' (all participants).
shared
boolean | 'owners' | string[]
When truthy, starting/stopping the integration is synchronized for all or a subset of participants.
Feature policy for the iframe (maps to the HTML allow attribute).
Sandbox restrictions for the iframe.
Whether to load the integration eagerly or lazily. Default: 'lazy'.
Starting and stopping integrations
// Start one or more integrations
call.startCustomIntegrations('my-app');
call.startCustomIntegrations(['my-app', 'another-app']);
// Stop integrations
call.stopCustomIntegrations('my-app');
Add custom buttons to Prebuilt’s control bar.
call.updateCustomTrayButtons({
'raise-hand': {
iconPath: 'https://example.com/icons/hand.svg',
iconPathDarkMode: 'https://example.com/icons/hand-dark.svg',
label: 'Raise hand',
tooltip: 'Raise or lower your hand',
visualState: 'default',
},
});
// Read current tray buttons
const buttons = call.customTrayButtons();
URL to an SVG or image file used as the button icon in light mode.
URL to an alternative icon for dark mode. Falls back to iconPath if not set.
Accessible label for the button.
Tooltip text shown on hover.
visualState
'default' | 'sidebar-open' | 'active'
Current visual state of the button. Use 'active' to show a pressed/toggled style, 'sidebar-open' to indicate an open sidebar.
call.on('custom-button-click', ({ button_id }) => {
if (button_id === 'raise-hand') {
toggleHandRaise();
// Update the button's visual state
const currentButtons = call.customTrayButtons();
const isActive = currentButtons['raise-hand'].visualState === 'active';
call.updateCustomTrayButtons({
'raise-hand': {
...currentButtons['raise-hand'],
visualState: isActive ? 'default' : 'active',
},
});
}
});
Example: light and dark theme
const call = Daily.createFrame({
theme: {
light: {
colors: {
accent: '#3B82F6',
accentText: '#FFFFFF',
background: '#FFFFFF',
backgroundAccent: '#F1F5F9',
baseText: '#0F172A',
border: '#E2E8F0',
mainAreaBg: '#F8FAFC',
mainAreaBgAccent: '#E2E8F0',
mainAreaText: '#0F172A',
supportiveText: '#64748B',
},
},
dark: {
colors: {
accent: '#60A5FA',
accentText: '#0F172A',
background: '#0F172A',
backgroundAccent: '#1E293B',
baseText: '#F1F5F9',
border: '#334155',
mainAreaBg: '#020617',
mainAreaBgAccent: '#0F172A',
mainAreaText: '#F1F5F9',
supportiveText: '#94A3B8',
},
},
},
});
The Prebuilt UI will automatically switch between light and dark variants based on the user’s operating system preference (prefers-color-scheme).