This document explains how themes are fetched and managed in the Fynd Commerce App, including GraphQL queries, data flow, and integration with the FPI system.
The theme fetching system:
- Uses GraphQL queries to fetch theme data from the server
- Integrates with FPI (Fynd Platform Integration) for data management
- Provides real-time theme updates
- Handles theme caching and state management
- Supports different types of theme queries
// src/graphql/queries/theme.query.ts
export const THEME_CONFIG_QUERY = `query theme($themeId: String!) {
theme(themeId: $themeId) {
preview_theme {
id
applied
created_at
styles
tags
updated_at
version
is_private
application_id
marketplace_theme_id
name
template_theme_id
theme_type
company_id
src
config {
list {
global_config
name
}
global_schema {
props {
category
id
info
label
type
}
}
current
}
}
theme_pages {
id
path
props
text
theme
type
value
created_at
updated_at
}
}
}`;export const APPLIED_THEME_QUERY = `query appliedTheme {
appliedTheme {
id
applied
available_sections {
blocks
label
name
props
}
config {
current
}
created_at
font {
family
}
styles
tags
updated_at
version
is_private
application_id
marketplace_theme_id
meta {
description
industry
slug
name
}
name
template_theme_id
theme_type
company_id
src
}
}`;export const THEME_DATA = `query Theme($themeId: String!, $pageValue: String!){
theme(themeId: $themeId) {
theme_page_detail(pageValue: $pageValue) {
id
path
props
sections {
label
name
id
custom_css
source {
type
id
bundle_name
}
assets {
js
css
}
blocks
predicate {
zones
route {
exact_url
query
selected
}
screen {
desktop
mobile
tablet
}
platform{
ios
android
web
}
user {
anonymous
authenticated
}
}
props
preset {
blocks {
type
name
props
}
}
}
sections_meta {
attributes
}
seo {
description
title
}
text
theme
type
value
}
}
}`;// In ThemeProvider
const {fpi} = useFpi();
const themeConfig: ThemeDetail = useSelector(fpi?.getters?.THEME);
// Theme is automatically fetched when FPI client is initialized
useEffect(() => {
if (fpi && !themeConfig) {
// Fetch applied theme
fpi.executeGQL(APPLIED_THEME_QUERY, {});
}
}, [fpi, themeConfig]);// Theme data is processed when received
const updateTheme = useCallback(() => {
if (themeConfig) {
try {
const themeObject = getThemeObject(themeConfig);
setTheme(prev => ({
...prev,
...themeObject,
}));
setLoading(false);
} catch (error) {
Logger.error('Failed to update theme state', {
error: error instanceof Error ? error.message : String(error),
themeConfig: themeConfig,
});
}
}
}, [getThemeObject, themeConfig]);// In navigation tracker
const onRouteChange = useCallback(
async (route: NavigationRoute) => {
if (route.name === 'SectionPage' && route.params?.pageValue) {
const pageValueResult = getPageValueFromPath(route.params.pageValue);
try {
await fpi.executeGQL(THEME_DATA, {
themeId: theme?.id,
pageValue: pageValueResult.pageValue,
});
} catch (error) {
Logger.error('Failed to fetch theme data', {
pageValue: pageValueResult.pageValue,
themeId: theme?.id,
error: error instanceof Error ? error.message : 'Unknown error',
});
}
}
},
[fpi, getPageValueFromPath, theme?.id],
);// Applied theme response
{
appliedTheme: {
id: string;
applied: boolean;
config: {
current: string;
};
font: {
family: string;
};
// ... other properties
}
}
// Theme configuration response
{
theme: {
preview_theme: {
config: {
list: [
{
name: string;
global_config: {
custom: { props: object };
static: { props: object };
};
}
];
current: string;
};
};
theme_pages: [
{
id: string;
path: string;
props: object;
// ... other properties
}
];
}
}// Theme data in Redux store
{
THEME: {
id: string;
applied: boolean;
config: {
list: Array<{
name: string;
global_config: {
custom: {props: object};
static: {props: object};
};
}>;
current: string;
}
font: {
family: string;
}
// ... other properties
}
}// FPI client provides theme data through Redux store
const {fpi} = useFpi();
const themeConfig: ThemeDetail = useSelector(fpi?.getters?.THEME);// Execute theme queries using FPI client
const fetchTheme = async () => {
try {
const result = await fpi.executeGQL(APPLIED_THEME_QUERY, {});
Logger.info('Theme fetched successfully', result);
} catch (error) {
Logger.error('Failed to fetch theme', error);
}
};
const fetchThemeData = async (themeId: string, pageValue: string) => {
try {
const result = await fpi.executeGQL(THEME_DATA, {
themeId,
pageValue,
});
Logger.info('Theme data fetched successfully', result);
} catch (error) {
Logger.error('Failed to fetch theme data', error);
}
};// Theme state is managed through Redux
const themeConfig = useSelector(fpi?.getters?.THEME);
// Theme updates are automatically handled by Redux
useEffect(() => {
if (themeConfig) {
updateTheme();
}
}, [themeConfig, updateTheme]);// Theme data is cached in Redux store
const themeConfig = useSelector(fpi?.getters?.THEME);
// Only fetch if not already available
useEffect(() => {
if (fpi && !themeConfig) {
fpi.executeGQL(APPLIED_THEME_QUERY, {});
}
}, [fpi, themeConfig]);// Only update theme when configuration changes
const updateTheme = useCallback(() => {
if (themeConfig && themeConfig !== previousThemeConfig) {
const themeObject = getThemeObject(themeConfig);
setTheme(themeObject);
setPreviousThemeConfig(themeConfig);
}
}, [themeConfig, previousThemeConfig, getThemeObject]);// Memoize query execution to prevent unnecessary requests
const memoizedThemeQuery = useMemo(() => {
return fpi.executeGQL.bind(fpi, APPLIED_THEME_QUERY, {});
}, [fpi]);const fetchTheme = async () => {
try {
const result = await fpi.executeGQL(APPLIED_THEME_QUERY, {});
return result;
} catch (error) {
Logger.error('Theme query failed', {
error: error instanceof Error ? error.message : String(error),
query: APPLIED_THEME_QUERY,
});
// Fallback to default theme
return null;
}
};const validateThemeData = (data: any): boolean => {
if (!data?.appliedTheme) {
Logger.warn('Invalid theme data structure', data);
return false;
}
if (!data.appliedTheme.config?.current) {
Logger.warn('Missing theme configuration', data);
return false;
}
return true;
};const fetchThemeWithRetry = async (retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
const result = await fpi.executeGQL(APPLIED_THEME_QUERY, {});
return result;
} catch (error) {
Logger.warn(`Theme fetch attempt ${i + 1} failed`, error);
if (i === retries - 1) {
throw error;
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
};// Check FPI client
console.log('FPI client available:', !!fpi);
// Check theme data
console.log('Theme config:', themeConfig);
// Check Redux store
console.log('Redux state:', fpi?.getters?.THEME);// Test theme query
const testThemeQuery = async () => {
try {
const result = await fpi.executeGQL(APPLIED_THEME_QUERY, {});
console.log('Theme query result:', result);
} catch (error) {
console.error('Theme query error:', error);
}
};// Validate theme data structure
const debugThemeData = () => {
console.log('Theme config structure:', {
hasConfig: !!themeConfig?.config,
configList: themeConfig?.config?.list,
currentConfig: themeConfig?.config?.current,
hasFont: !!themeConfig?.font,
});
};// Enable theme fetching debugging
const debugThemeFetching = () => {
console.log('๐ Theme Fetching Debug:', {
fpiAvailable: !!fpi,
themeConfig: themeConfig,
loading,
hasTheme: !!theme,
});
};
// Use in development
if (__DEV__) {
debugThemeFetching();
}- Use specific queries for different use cases
- Implement proper error handling
- Add retry logic for network failures
- Use Redux for global theme state
- Implement proper loading states
- Handle theme updates efficiently
- Cache theme data appropriately
- Minimize unnecessary queries
- Use memoization for expensive operations
- Provide fallback themes
- Log errors for debugging
- Implement graceful degradation
- Advanced Caching: More sophisticated caching strategies
- Real-time Updates: WebSocket-based theme updates
- Offline Support: Offline theme caching and sync
- Theme Analytics: Theme usage and performance analytics
This documentation covers theme fetching and management. For more specific implementation details, refer to the other theme documentation sections.
