The config.json file handles basic feature toggles. For deeper customizations, modify the Zustand store, theme configuration, or component props directly.
Understanding the Customization Architecture
The React Native UI Kit Builder uses these main files for customization:
| File | Purpose | When to Modify |
|---|
config.json | Feature flags and configuration constants | Functional changes (enable/disable features) |
store.ts | Zustand store for state management | Runtime configuration updates |
| Theme object | Colors, typography, and styling | UI/visual changes |
The config.json file is the source of truth for your Builder configuration. You can update it manually or by scanning a QR code with new settings.
Using the Configuration Store
The Zustand store manages your Builder configuration at runtime. Access it anywhere in your app to read or update settings.
Reading Configuration
import { useConfig } from './src/config/store';
const MyComponent = () => {
// Access entire settings object
const settings = useConfig((state) => state.settings);
// Access specific feature category
const chatFeatures = useConfig((state) => state.settings.chatFeatures);
// Access style configuration
const styleConfig = useConfig((state) => state.settings.style);
return (/* ... */);
};
Updating Configuration at Runtime
import { useConfigStore } from './src/config/store';
// Update a specific setting
const updateFeature = () => {
const store = useConfigStore.getState();
store.updateSettings({
...store.settings,
chatFeatures: {
...store.settings.chatFeatures,
coreMessagingExperience: {
...store.settings.chatFeatures.coreMessagingExperience,
photosSharing: false,
},
},
});
};
Runtime changes to the store are not persisted by default. Use AsyncStorage to save and restore configurations.
Theme Customization
Applying Builder Theme to UI Kit
The Builder configuration includes style settings that should be applied to the CometChat UI Kit theme. The getFontFamily helper used below is defined in Custom Font Integration; import it from wherever you place that utility.
import React from 'react';
import { CometChatThemeProvider } from '@cometchat/chat-uikit-react-native';
import { useConfig } from './src/config/store';
import { getFontFamily } from './src/utils/fonts'; // defined in "Custom Font Integration" below
const App = () => {
const styleConfig = useConfig((state) => state.settings.style);
const fontFamily = getFontFamily(styleConfig.typography.font);
const theme = {
light: {
color: {
primary: styleConfig.color.brandColor,
textPrimary: styleConfig.color.primaryTextLight,
textSecondary: styleConfig.color.secondaryTextLight,
background: '#FFFFFF',
border: '#E8E8E8',
},
},
dark: {
color: {
primary: styleConfig.color.brandColor,
textPrimary: styleConfig.color.primaryTextDark,
textSecondary: styleConfig.color.secondaryTextDark,
background: '#141414',
border: '#3D3D3D',
},
},
};
return (
<CometChatThemeProvider theme={theme}>
{/* Your app components */}
</CometChatThemeProvider>
);
};
The UI Kit’s typography theme tokens are keyed by named text styles (for example heading4) and each has bold, medium, and regular variants whose fontFamily is a string—not a single top-level fontFamily. To apply a Builder-selected font across the UI, set the fontFamily on the relevant typography tokens. See Component Styling for the exact token structure and Theming for the theme provider overview.
Custom Color Palette
Override the default colors by modifying the theme object:
const customTheme = {
light: {
color: {
primary: '#FF6B6B', // Custom brand color
textPrimary: '#2D3436', // Custom primary text
textSecondary: '#636E72', // Custom secondary text
success: '#00B894', // Success state
error: '#D63031', // Error state
warning: '#FDCB6E', // Warning state
},
},
dark: {
color: {
primary: '#FF6B6B',
textPrimary: '#DFE6E9',
textSecondary: '#B2BEC3',
success: '#00B894',
error: '#FF7675',
warning: '#FFEAA7',
},
},
};
Custom Font Integration
Step 1: Add Font Files
Add your custom font files to the project’s assets/fonts/ directory (and reference them from the platform font folders described in the Directory Structure guide):
- iOS:
ios/<App>/Resources/Fonts/
- Android:
android/app/src/main/assets/fonts/
Step 2: Register Fonts
React Native CLI (bare)
Expo (development build)
Native modules in React Native 0.60+ are linked automatically by autolinking, so the legacy react-native link command is no longer used. To copy your font assets into the native projects, declare them in react-native.config.js:module.exports = {
assets: ['./assets/fonts'],
};
Then run:Rebuild the app (npm run ios / npm run android) so the fonts are bundled. In an Expo development build, register fonts with the expo-font config plugin in app.json instead of react-native-asset, then rebuild the development client. Refer to the Expo Integration guide for the native setup CometChat requires under Expo.
Step 3: Map Font Family
Create a font mapping utility. getFontFamily returns the base font-family string for a given Builder font name, so it can be assigned to the UI Kit’s fontFamily typography tokens:
import { Platform } from 'react-native';
const FONT_MAP: Record<string, string> = {
'roboto': Platform.OS === 'ios' ? 'Roboto-Regular' : 'roboto_regular',
'inter': Platform.OS === 'ios' ? 'Inter-Regular' : 'inter_regular',
'your-custom-font': Platform.OS === 'ios' ? 'YourFont-Regular' : 'your_font_regular',
};
export const getFontFamily = (fontName: string): string => {
return FONT_MAP[fontName] || FONT_MAP['roboto'];
};
The exact font-family name must match the PostScript name of the installed font (iOS) or the file name without extension (Android). Verify the registered names after running the registration step above.
Component-Level Customizations
Conditional Rendering Based on Features
Use the configuration store to conditionally render UI elements:
import { useConfig } from './src/config/store';
const MessageComposer = () => {
const chatFeatures = useConfig((state) => state.settings.chatFeatures);
const { coreMessagingExperience, deeperUserEngagement } = chatFeatures;
return (
<View>
{/* Always show text input */}
<TextInput placeholder="Type a message..." />
{/* Conditionally show attachment options */}
{coreMessagingExperience.photosSharing && (
<PhotoAttachmentButton />
)}
{coreMessagingExperience.fileSharing && (
<FileAttachmentButton />
)}
{deeperUserEngagement.voiceNotes && (
<VoiceNoteButton />
)}
{deeperUserEngagement.stickers && (
<StickerButton />
)}
</View>
);
};
Customizing Message Options
Control which message options appear based on configuration:
const MessageOptions = ({ message }) => {
const chatFeatures = useConfig((state) => state.settings.chatFeatures);
const { coreMessagingExperience, deeperUserEngagement, moderatorControls } = chatFeatures;
const options = [];
if (coreMessagingExperience.quotedReplies) {
options.push({ label: 'Reply', action: 'reply' });
}
if (coreMessagingExperience.threadConversationAndReplies) {
options.push({ label: 'Reply in Thread', action: 'thread' });
}
if (deeperUserEngagement.reactions) {
options.push({ label: 'React', action: 'react' });
}
if (coreMessagingExperience.editMessage && message.sender.uid === currentUser.uid) {
options.push({ label: 'Edit', action: 'edit' });
}
if (coreMessagingExperience.deleteMessage && message.sender.uid === currentUser.uid) {
options.push({ label: 'Delete', action: 'delete' });
}
if (moderatorControls.reportMessage) {
options.push({ label: 'Report', action: 'report' });
}
return <OptionsMenu options={options} />;
};
Persisting Configuration
Save Configuration to AsyncStorage
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useConfigStore } from './src/config/store';
const CONFIG_KEY = '@cometchat_builder_config';
export const saveConfig = async () => {
try {
const config = useConfigStore.getState();
await AsyncStorage.setItem(CONFIG_KEY, JSON.stringify(config));
} catch (error) {
console.error('Failed to save config:', error);
}
};
export const loadConfig = async () => {
try {
const savedConfig = await AsyncStorage.getItem(CONFIG_KEY);
if (savedConfig) {
const parsed = JSON.parse(savedConfig);
useConfigStore.getState().updateConfig(parsed);
}
} catch (error) {
console.error('Failed to load config:', error);
}
};
Auto-save on Configuration Changes
import { useEffect } from 'react';
import { useConfigStore } from './src/config/store';
const ConfigPersistence = () => {
const config = useConfigStore((state) => state);
useEffect(() => {
// Debounce saves to avoid excessive writes
const timeoutId = setTimeout(() => {
saveConfig();
}, 1000);
return () => clearTimeout(timeoutId);
}, [config]);
return null;
};
Layout Customization
Dynamic Tab Configuration
import { useConfig } from './src/config/store';
const TabNavigator = () => {
const layout = useConfig((state) => state.settings.layout);
const { tabs, withSideBar } = layout;
if (!withSideBar) {
return <SingleChatView />;
}
return (
<Tab.Navigator>
{tabs.includes('chats') && (
<Tab.Screen name="Chats" component={ChatsScreen} />
)}
{tabs.includes('calls') && (
<Tab.Screen name="Calls" component={CallsScreen} />
)}
{tabs.includes('users') && (
<Tab.Screen name="Users" component={UsersScreen} />
)}
{tabs.includes('groups') && (
<Tab.Screen name="Groups" component={GroupsScreen} />
)}
</Tab.Navigator>
);
};
Going Beyond the Builder
The Builder config covers feature toggles, layout, and high-level theme tokens. For deeper customization, drop down to the UI Kit’s own customization layers—these work the same whether or not you use the Builder:
Message Templates
Override how message bubbles render with CometChatMessageTemplate and the templates prop on the message list.
Component Styling & View Slots
Style individual components and inject custom views (headers, subtitles, list items) via theme overrides and slot props.
Theme Overrides
Customize colors, typography tokens, and light/dark modes through CometChatThemeProvider.
Builder Settings Reference
Full schema for every config.json key the Builder exports.