Skip to content

Customize the UI

Learn how to customize the Pika Framework user interface, configure features, and extend functionality using designated customization areas that are protected from framework updates.

By the end of this guide, you will:

  • Configure project names and branding
  • Customize site-wide features
  • Configure home page experience
  • Add custom message tag renderers
  • Implement custom authentication
  • Understand protected customization areas
  • Manage sync configuration
  • A running Pika installation
  • Access to project configuration files
  • Understanding of SvelteKit and TypeScript (for advanced customization)
  • Familiarity with AWS CDK (for infrastructure changes)

Pika Framework provides designated areas where you can add custom code without worrying about framework updates overwriting your changes. These areas are protected from sync operations.

Step 1: Configure Project Names and Branding

Section titled “Step 1: Configure Project Names and Branding”

All customization starts with configuring your project identity.

Location: pika-config.ts (root directory)

This file is the single source of truth for project names and is protected from framework updates.

export const pikaConfig: PikaConfig = {
pika: {
projNameL: 'mycompany', // All lowercase
projNameKebabCase: 'mycompany', // Kebab case
projNameTitleCase: 'MyCompany', // Title case
projNameCamel: 'mycompany', // Camel case
projNameHuman: 'My Company' // Human readable
},
pikaChat: {
projNameL: 'mycompanychat', // All lowercase
projNameKebabCase: 'mycompany-chat', // Kebab case
projNameTitleCase: 'MyCompanyChat', // Title case
projNameCamel: 'myCompanyChat', // Camel case
projNameHuman: 'My Company Chat' // Human readable
}
};

Why This Matters:

  • All AWS resources use these names
  • Stack definitions automatically import these values
  • Ensures consistent naming across your infrastructure

Configure the home page appearance, branding, and assistant visibility.

Location: pika-config.ts (root directory)

The home page has several customizable areas:

┌─────────────────────────────────────────────────────────────┐
│ [logo] homePageTitle [⚙️] │ ← Header
├─────────────────────────────────────────────────────────────┤
│ │
│ Choose an AI Assistant │ ← Hero heading
│ subtitle │ ← Subtitle
│ │
│ ┌─────────────────────────────────┐ │
│ │ 🔍 Search assistants... │ │ ← Search (auto at 6+)
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ [icon] │ │ [icon] │ │ ← Assistant cards
│ │ Assistant Name │ │ Assistant Name │ │ with icons
│ │ Description... │ │ Description... │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
export const pikaConfig: PikaConfig = {
// ... project names configuration
siteFeatures: {
homePage: {
// ═══════════════════════════════════════════════════════
// HEADER BRANDING
// ═══════════════════════════════════════════════════════
// Title shown in header next to logo
homePageTitle: 'Acme AI Assistants',
// Logo configuration (defaults to Pika logo)
logo: '/custom/assets/acme-logo.svg',
// Or for light/dark mode support:
// logo: { light: '/custom/assets/logo-dark.svg', dark: '/custom/assets/logo-light.svg' },
// Or to hide: logo: null,
logoHeight: 48, // Logo height in pixels (width scales automatically)
logoGap: 16, // Space between logo and title in pixels
// ═══════════════════════════════════════════════════════
// HERO SECTION
// ═══════════════════════════════════════════════════════
// Subtitle below "Choose an AI Assistant"
subtitle: 'Intelligent tools to help you work smarter',
// Search bar visibility: true, false, or 'auto' (shows when 6+ assistants)
searchEnabled: 'auto',
// ═══════════════════════════════════════════════════════
// ASSISTANT CARD ICONS
// ═══════════════════════════════════════════════════════
// Default icon for all assistant cards (individual assistants can override)
defaultAssistantIcon: '/custom/assets/ai-assistant-icon.svg',
// Icon size inside the card's icon container (default: 24px)
assistantIconSize: 28,
// ═══════════════════════════════════════════════════════
// NAVIGATION
// ═══════════════════════════════════════════════════════
// Text on sidebar button when inside a chat (returns to home)
navigationButtonText: 'AI Assistants',
// ═══════════════════════════════════════════════════════
// ACCESS CONTROL
// ═══════════════════════════════════════════════════════
// Rules for which users see which assistants
linksToChatApps: {
userChatAppRules: [
// External users see only external-facing assistants
{
userTypes: ['external-user'],
chatAppUserTypes: ['external-user']
},
// Internal users see all assistants
{
userTypes: ['internal-user'],
chatAppUserTypes: ['internal-user', 'external-user']
}
]
}
}
}
};
OptionDescriptionDefault
homePageTitleTitle displayed in the header next to logo"AI Assistants"
subtitleDescriptive text below "Choose an AI Assistant""Select an assistant to get started"
logoLogo URL, { light, dark } object, or null to hidePika logo
logoHeightLogo height in pixels (width auto-scales)48
logoGapSpace between logo and title in pixels16
navigationButtonTextSidebar button text to return to home"AI Assistants"
searchEnabledSearch bar: true, false, or 'auto''auto'
defaultAssistantIconDefault icon URL for assistant cardsSparkle icon
assistantIconSizeIcon size in pixels inside card container24
linksToChatAppsRules for which users see which assistants

By default, the Pika logo appears in the header. Replace it with your own branding.

Option 1: Single logo (same for light/dark modes)

homePage: {
logo: '/custom/assets/my-company-logo.svg',
logoHeight: 48, // Adjust to fit your logo
logoGap: 16, // Space between logo and title
}

Option 2: Separate logos for light/dark modes

homePage: {
logo: {
light: '/custom/assets/logo-dark.svg', // Dark logo on light background
dark: '/custom/assets/logo-light.svg' // Light logo on dark background
},
logoHeight: 48,
}

Option 3: No logo (title only)

homePage: {
logo: null,
}

Each assistant card displays an icon. You can customize at two levels:

┌─────────────────────────────────────┐
│ ┌──────┐ │
│ │ icon │ Assistant Title │ ← Icon shown here
│ └──────┘ │
│ Description text goes here... │
│ │
│ Start Chat → │
└─────────────────────────────────────┘

Icon specifications:

  • Recommended size: 40×40 pixels (80×80 for retina displays)
  • Display size: Controlled by assistantIconSize (default 24px inside 40px container)
  • Format: SVG recommended (scales perfectly), PNG/JPG/WebP supported
  • Style: Simple, single-color icons work best (inherits theme colors)

Set a default icon for all assistants that don't have their own:

// In pika-config.ts
homePage: {
defaultAssistantIcon: '/custom/assets/ai-assistant-icon.svg',
assistantIconSize: 28, // Make icons slightly larger (default: 24)
}

Override the default for specific assistants in your chat app definition:

// In your chat app configuration
{
chatAppId: 'order-assistant',
title: 'Order Assistant',
description: 'Track orders and analyze order data',
agentId: 'order-agent',
icon: '/custom/assets/order-icon.svg', // Overrides global default
}

Icon priority: Per-assistant icon → Global default → Built-in sparkle icon


Control which users see which AI assistants:

  1. System checks the user's type (internal-user or external-user)
  2. Finds matching rules in userChatAppRules
  3. Shows assistants whose userTypes match the rule's chatAppUserTypes
  4. If no rules match, no assistants appear
linksToChatApps: {
userChatAppRules: [
// Example: External users only see customer-facing assistants
{
userTypes: ['external-user'],
chatAppUserTypes: ['external-user']
},
// Example: Internal users see everything
{
userTypes: ['internal-user'],
chatAppUserTypes: ['internal-user', 'external-user']
}
]
}

Configure features that affect the entire application.

Location: pika-config.ts (root directory)

siteFeatures: {
// Show AI reasoning traces
traces: {
featureId: 'traces',
enabled: true,
userTypes: ['internal-user'],
detailedTraces: {
enabled: true,
userTypes: ['internal-user'],
userRoles: ['pika:content-admin']
}
},
// Verify and improve response quality
verifyResponse: {
featureId: 'verifyResponse',
enabled: true,
autoRepromptThreshold: 'C', // Auto-improve responses graded C or lower
userTypes: ['internal-user', 'external-user']
},
// User memory for context
userMemory: {
featureId: 'userMemory',
enabled: true,
strategy: 'semantic',
conversationMemoryStrategy: 'preferences'
}
}
siteFeatures: {
// Enable logout
logout: {
featureId: 'logout',
enabled: true,
userTypes: ['internal-user', 'external-user']
},
// Enable file upload
fileUpload: {
featureId: 'fileUpload',
enabled: true,
mimeTypesAllowed: [
'image/*',
'application/pdf',
'text/plain',
'.docx',
'.xlsx'
]
},
// Chat suggestions
suggestions: {
featureId: 'suggestions',
enabled: true,
suggestions: [], // Chat apps define their own
maxToShow: 5,
randomize: false
},
// Input field label
promptInputFieldLabel: {
featureId: 'promptInputFieldLabel',
enabled: true,
promptInputFieldLabel: 'Ready to chat'
},
// UI customization
uiCustomization: {
featureId: 'uiCustomization',
enabled: true,
showUserRegionInLeftNav: false,
showChatHistoryInStandaloneMode: true
},
// Disclaimer notice
chatDisclaimerNotice: {
featureId: 'chatDisclaimerNotice',
enabled: true,
notice: 'This AI-powered chat is here to help, but it may not always be accurate. For urgent issues, please contact support.'
}
}
siteFeatures: {
// Site admin interface
siteAdmin: {
websiteEnabled: true
// Users need 'pika:site-admin' role
},
// Content admin access
contentAdmin: {
enabled: true
// Users need 'pika:content-admin' role
},
// Entity-based access
entity: {
enabled: true,
attributeName: 'accountId',
searchPlaceholderText: 'Search for an account...',
displayNameSingular: 'Account',
displayNamePlural: 'Accounts'
}
}

Create custom renderers for XML tags in LLM responses.

Location: apps/pika-chat/src/lib/client/features/chat/message-segments/custom-components/

CustomDataTable.svelte
<script lang="ts">
export let data: { headers: string[]; rows: string[][] };
</script>
<div class="custom-table">
<table>
<thead>
<tr>
{#each data.headers as header}
<th>{header}</th>
{/each}
</tr>
</thead>
<tbody>
{#each data.rows as row}
<tr>
{#each row as cell}
<td>{cell}</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
<style>
.custom-table {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 0.5rem;
border: 1px solid #ddd;
text-align: left;
}
th {
background-color: #f5f5f5;
font-weight: bold;
}
</style>
custom-renderers.ts
import CustomDataTable from './CustomDataTable.svelte';
export const customRenderers: Record<string, Component<any>> = {
'data-table': CustomDataTable
};
<data-table>
{
"headers": ["Name", "Age", "City"],
"rows": [
["John", "30", "NYC"],
["Jane", "25", "LA"]
]
}
</data-table>

Configure custom authentication flows for your SSO or auth provider.

Location: apps/pika-chat/src/lib/server/auth-provider/

import { AuthProvider } from 'pika-shared/types/chatbot/chatbot-types';
import type { AuthenticatedUser } from 'pika-shared/types/chatbot/chatbot-types';
export default class CustomAuthProvider extends AuthProvider<YourAuthData, YourCustomData> {
async getUser(event: RequestEvent): Promise<AuthenticatedUser | undefined> {
// Your authentication logic
const token = event.cookies.get('auth_token');
if (!token) return undefined;
const userData = await validateToken(token);
if (!userData) return undefined;
return {
userId: userData.id,
firstName: userData.firstName,
lastName: userData.lastName,
email: userData.email,
userType: userData.isEmployee ? 'internal-user' : 'external-user',
roles: this.assignRoles(userData),
customData: {
accountId: userData.accountId,
permissions: userData.permissions
}
};
}
private assignRoles(userData: any): string[] {
const roles: string[] = [];
if (userData.isAdmin) {
roles.push('pika:site-admin');
}
if (userData.isSupport) {
roles.push('pika:content-admin');
}
return roles;
}
}

Create new web applications within your Pika project.

Location: apps/custom/

Terminal window
# Create new SvelteKit app
cd apps/custom
npm create svelte@latest my-admin-panel
# Install dependencies
cd my-admin-panel
npm install

Use Cases:

  • Admin dashboards
  • Customer portals
  • Internal tools
  • Separate applications for different user types

Create new backend services and API endpoints.

Location: services/custom/

Use Cases:

  • Payment processing services
  • Email notification services
  • Data analytics services
  • Third-party integrations

These areas are never overwritten by framework updates:

  1. Project Configuration - pika-config.ts
  2. Custom Message Tag Renderers - apps/pika-chat/src/lib/client/features/chat/message-segments/custom-components/
  3. Authentication - apps/pika-chat/src/lib/server/auth-provider/
  4. Custom Web Applications - apps/custom/
  5. Custom Services - services/custom/
  6. Stack Definition Files (by default):
    • apps/pika-chat/infra/bin/pika-chat.ts
    • services/pika/bin/pika.ts

Control which files are protected from framework updates.

Location: .pika-sync.json (root directory)

{
"userProtectedAreas": [
"my-custom-config.ts",
"apps/my-custom-app/"
],
"userUnprotectedAreas": [
"apps/pika-chat/infra/bin/pika-chat.ts",
"services/pika/bin/pika.ts"
]
}

Configuration:

  • userProtectedAreas - Additional files you want to protect
  • userUnprotectedAreas - Default protected files you want to allow updates for

Features follow this precedence order:

  1. Site Level (in pika-config.ts)
  2. Chat App Override (in chat app definition)
  3. Admin Override (via Site Admin interface)

Rules:

  • Site-level settings are the baseline
  • Chat apps can make features more restrictive
  • Admin overrides completely replace chat app settings
  • No level can enable site-disabled features

Before customizing anything else, update project names in pika-config.ts. This ensures consistent naming across all AWS resources.

Always place custom code in designated customization areas. Files outside these areas may be overwritten during framework updates.

Use the appropriate area for your custom code:

TypeLocation
Project namespika-config.ts
UI componentsCustom Components
Auth logicCustom Authentication
New appsCustom Web Applications
Backend servicesCustom Services
InfrastructureStack Definition Files

Initialize git and commit customizations regularly:

Terminal window
git init
git add .
git commit -m "Initial commit with customizations"

After running pika sync, always test customizations to ensure they still work correctly.

Keep documentation of customizations, especially for complex changes to stack definitions or custom services.

When you run pika sync:

  1. Framework files get latest changes
  2. All files in protected areas are preserved
  3. Your userProtectedAreas merge with defaults
  4. Files in userUnprotectedAreas can be updated

This ensures customizations are preserved while receiving framework improvements.

// Update project names
pikaConfig.pikaChat.projNameHuman = 'Acme Corp Assistant';
// Configure home page
siteFeatures.homePage = {
homePageTitle: 'Acme Corp AI Assistant',
welcomeMessage: 'Welcome! How can I help you today?'
};
// Add custom CSS in app.css for brand colors
// Enable entity feature
siteFeatures.entity = {
enabled: true,
attributeName: 'companyId',
displayNameSingular: 'Company',
displayNamePlural: 'Companies'
};
// Enable site admin for access control
siteFeatures.siteAdmin = {
websiteEnabled: true
};
// Configure access rules per tenant in Site Admin interface
// Restrict to internal users
siteFeatures.logout = {
enabled: true,
userTypes: ['internal-user']
};
// Enable all admin features
siteFeatures.contentAdmin = { enabled: true };
siteFeatures.siteAdmin = { websiteEnabled: true };
siteFeatures.traces = {
enabled: true,
userTypes: ['internal-user']
};