Screen Tagging
📊 Phase 2: Screen Analytics (Tagging)
Overview
Screen tagging transforms raw session recordings into actionable analytics by providing meaningful names for each user interface. Proper screen tagging is essential for Phase 3 privacy protection and enables heat maps, funnels, and journey analysis.
Estimated time: 1-2 hours
Prerequisites: Phase 1 (Bootstrap Setup) completed
Why Screen Tagging Matters
Good screen names unlock powerful UXCam features:
- Heat Maps: Understand user interaction patterns per screen
- Conversion Funnels: Track user progress through key flows
- Journey Analytics: Visualize complete user paths
- Privacy Protection: Enable screen-based occlusion rules (Phase 3)
- Performance Analysis: Measure screen load times and engagement
1. Screen Tagging Strategy
Manual vs Automatic Tagging
Important: For React Native, manual tagging is strongly recommended over automatic tagging due to React Native's navigation architecture.
// Always set this in your configuration
const configuration = {
userAppKey: 'YOUR_API_KEY',
enableAutomaticScreenNameTagging: false, // Manual control recommended
enableImprovedScreenCapture: true,
};
Why Manual Tagging?
- React Native navigation doesn't map 1:1 with native Activities/ViewControllers
- Provides consistent naming across iOS and Android
- Enables meaningful screen names instead of technical component names
- Required for effective privacy protection in Phase 3
Screen Naming Best Practices
// ✅ Good screen names
RNUxcam.tagScreenName('Home Dashboard');
RNUxcam.tagScreenName('Product Details');
RNUxcam.tagScreenName('Checkout Payment');
RNUxcam.tagScreenName('User Profile Settings');
// ❌ Avoid technical names
RNUxcam.tagScreenName('HomeComponent');
RNUxcam.tagScreenName('Screen1');
RNUxcam.tagScreenName('com.app.ProductView');
Naming Conventions:
- Use descriptive, human-readable names
- Include the primary function or content type
- Keep names consistent across similar screens
- Avoid special characters and numbers when possible
- Consider how names will appear in analytics dashboards
2. Navigation Framework Integration
React Navigation v6 (Recommended)
React Navigation is the most popular navigation library and integrates seamlessly with UXCam:
import React from 'react';
import { useFocusEffect } from '@react-navigation/native';
import RNUxcam from 'react-native-ux-cam';
function HomeScreen() {
useFocusEffect(
React.useCallback(() => {
RNUxcam.tagScreenName('Home Dashboard');
}, [])
);
return (
// Your screen content
);
}
export default HomeScreen;
Centralized Screen Tagging Hook
import { useFocusEffect } from '@react-navigation/native';
import RNUxcam from 'react-native-ux-cam';
import { useCallback } from 'react';
export const useScreenTracking = (screenName) => {
useFocusEffect(
useCallback(() => {
if (screenName) {
RNUxcam.tagScreenName(screenName);
}
}, [screenName])
);
};
// Usage in screens
function ProductScreen() {
useScreenTracking('Product Details');
return (
// Screen content
);
}
Tab Navigator Handling
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { useFocusEffect } from '@react-navigation/native';
const Tab = createBottomTabNavigator();
function TabScreen({ route }) {
useFocusEffect(
React.useCallback(() => {
// Tag based on active tab
const tabName = route.name;
RNUxcam.tagScreenName(`${tabName} Tab`);
}, [route.name])
);
return (
// Tab content
);
}
Stack Navigator with Parameters
import { useFocusEffect, useRoute } from '@react-navigation/native';
function ProductDetailsScreen() {
const route = useRoute();
useFocusEffect(
React.useCallback(() => {
// Include relevant parameters in screen name
const { productId, category } = route.params || {};
const screenName = productId ?
`Product Details - ${category}` :
'Product Details';
RNUxcam.tagScreenName(screenName);
}, [route.params])
);
return (
// Screen content
);
}
Expo Router Integration
import { useSegments, usePathname } from 'expo-router';
import { useEffect } from 'react';
import RNUxcam from 'react-native-ux-cam';
export default function HomeScreen() {
const segments = useSegments();
const pathname = usePathname();
useEffect(() => {
// Convert file-based routing to meaningful screen names
const getScreenName = (path, segments) => {
if (path === '/') return 'Home Dashboard';
if (path.includes('/profile')) return 'User Profile';
if (path.includes('/products')) return 'Product Catalog';
// Fallback to segments
return segments.join(' ').replace(/[()]/g, '') || 'Unknown Screen';
};
const screenName = getScreenName(pathname, segments);
RNUxcam.tagScreenName(screenName);
}, [pathname, segments]);
return (
// Screen content
);
}
Expo Router Layout Integration
import { useSegments } from 'expo-router';
import { useEffect } from 'react';
import RNUxcam from 'react-native-ux-cam';
export default function RootLayout() {
const segments = useSegments();
useEffect(() => {
// Global screen tracking for all routes
if (segments.length > 0) {
const screenName = formatScreenName(segments);
RNUxcam.tagScreenName(screenName);
}
}, [segments]);
const formatScreenName = (segments) => {
// Convert file paths to readable names
const nameMap = {
'(tabs)': '',
'home': 'Home Dashboard',
'profile': 'User Profile',
'settings': 'Settings',
'[id]': 'Details'
};
return segments
.map(segment => nameMap[segment] || segment)
.filter(Boolean)
.join(' - ') || 'Home';
};
return (
// Layout content
);
}
React Native Navigation (Wix)
import { Navigation } from 'react-native-navigation';
import RNUxcam from 'react-native-ux-cam';
class HomeScreen extends Component {
constructor(props) {
super(props);
// Bind navigation events
Navigation.events().bindComponent(this);
}
componentDidAppear() {
// Tag screen when it appears
RNUxcam.tagScreenName('Home Dashboard');
}
componentDidDisappear() {
// Optional: Handle screen disappearance
}
render() {
return (
// Screen content
);
}
}
3. Advanced Screen Tagging Patterns
Modal and Overlay Screens
import { useEffect } from 'react';
import { Modal } from 'react-native';
import RNUxcam from 'react-native-ux-cam';
function ModalScreen({ visible, onClose }) {
useEffect(() => {
if (visible) {
// Tag modal as a separate screen
RNUxcam.tagScreenName('Settings Modal');
}
}, [visible]);
return (
<Modal visible={visible} onRequestClose={onClose}>
{/* Modal content */}
</Modal>
);
}
Dynamic Screen Names
import { useFocusEffect } from '@react-navigation/native';
import { useState, useEffect } from 'react';
function DynamicScreen({ route }) {
const [screenTitle, setScreenTitle] = useState('Loading...');
useFocusEffect(
React.useCallback(() => {
// Wait for dynamic content to load
if (screenTitle !== 'Loading...') {
RNUxcam.tagScreenName(`Dynamic - ${screenTitle}`);
}
}, [screenTitle])
);
useEffect(() => {
// Simulate API call for dynamic content
fetchScreenData().then(data => {
setScreenTitle(data.title);
});
}, []);
return (
// Screen content
);
}
Conditional Screen Tagging
import RNUxcam from 'react-native-ux-cam';
export const conditionalScreenTag = (baseScreenName, conditions = {}) => {
let screenName = baseScreenName;
// Add user state information
if (conditions.isLoggedIn) {
screenName += ' - Authenticated';
} else {
screenName += ' - Guest';
}
// Add feature flags
if (conditions.premiumUser) {
screenName += ' - Premium';
}
// Add A/B test variants
if (conditions.variant) {
screenName += ` - ${conditions.variant}`;
}
RNUxcam.tagScreenName(screenName);
};
// Usage
conditionalScreenTag('Product Details', {
isLoggedIn: user.isAuthenticated,
premiumUser: user.isPremium,
variant: abTestVariant,
});
4. WebView Screen Tagging
Basic WebView Integration
import { WebView } from 'react-native-webview';
import { useFocusEffect } from '@react-navigation/native';
import RNUxcam from 'react-native-ux-cam';
function WebViewScreen() {
useFocusEffect(
React.useCallback(() => {
RNUxcam.tagScreenName('WebView Content');
}, [])
);
const handleNavigationStateChange = (navState) => {
// Tag based on WebView URL changes
const { url, title } = navState;
if (url.includes('/checkout')) {
RNUxcam.tagScreenName('WebView - Checkout');
} else if (url.includes('/profile')) {
RNUxcam.tagScreenName('WebView - Profile');
} else if (title) {
RNUxcam.tagScreenName(`WebView - ${title}`);
}
};
return (
<WebView
source={{ uri: 'https://your-webapp.com' }}
onNavigationStateChange={handleNavigationStateChange}
/>
);
}
Advanced WebView Integration with JavaScript Bridge
import { WebView } from 'react-native-webview';
import RNUxcam from 'react-native-ux-cam';
function AdvancedWebView() {
const injectedJavaScript = `
// Inject UXCam screen tagging into WebView
window.tagUXCamScreen = function(screenName) {
window.ReactNativeWebView.postMessage(JSON.stringify({
type: 'SCREEN_TAG',
screenName: screenName
}));
};
// Auto-tag based on page changes
let lastPath = location.pathname;
setInterval(() => {
if (location.pathname !== lastPath) {
lastPath = location.pathname;
window.tagUXCamScreen('WebView - ' + document.title);
}
}, 1000);
true; // Required for injectedJavaScript
`;
const handleMessage = (event) => {
try {
const data = JSON.parse(event.nativeEvent.data);
if (data.type === 'SCREEN_TAG') {
RNUxcam.tagScreenName(data.screenName);
}
} catch (error) {
console.warn('WebView message parsing failed:', error);
}
};
return (
<WebView
source={{ uri: 'https://your-webapp.com' }}
injectedJavaScript={injectedJavaScript}
onMessage={handleMessage}
javaScriptEnabled={true}
/>
);
}
5. Screen Lifecycle Management
Timing Considerations
import RNUxcam from 'react-native-ux-cam';
class ScreenLifecycleManager {
static currentScreen = null;
static screenStartTime = null;
static enterScreen(screenName) {
// Track screen entry time
this.screenStartTime = Date.now();
this.currentScreen = screenName;
// Tag the screen
RNUxcam.tagScreenName(screenName);
if (__DEV__) {
console.log(`Entered screen: ${screenName}`);
}
}
static exitScreen() {
if (this.currentScreen && this.screenStartTime) {
const duration = Date.now() - this.screenStartTime;
if (__DEV__) {
console.log(`Exited screen: ${this.currentScreen} (${duration}ms)`);
}
// Log screen duration as event
RNUxcam.logEvent('Screen Duration', {
screen: this.currentScreen,
duration: duration
});
}
this.currentScreen = null;
this.screenStartTime = null;
}
}
// Usage in screens
function ScreenWithLifecycle() {
useFocusEffect(
React.useCallback(() => {
ScreenLifecycleManager.enterScreen('Product Details');
return () => {
ScreenLifecycleManager.exitScreen();
};
}, [])
);
return (
// Screen content
);
}
Debounced Screen Tagging
import { useRef, useCallback } from 'react';
import { useFocusEffect } from '@react-navigation/native';
import RNUxcam from 'react-native-ux-cam';
export const useDebouncedScreenTag = (screenName, delay = 300) => {
const timeoutRef = useRef(null);
const debouncedTag = useCallback(() => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
RNUxcam.tagScreenName(screenName);
}, delay);
}, [screenName, delay]);
useFocusEffect(
useCallback(() => {
debouncedTag();
return () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [debouncedTag])
);
};
6. Screen Tagging Validation
Development Tools
import RNUxcam from 'react-native-ux-cam';
class ScreenValidator {
static taggedScreens = new Set();
static screenDurations = new Map();
static duplicateDetector = new Map();
static tagScreen(screenName) {
// Validate screen name format
if (!screenName || screenName.trim().length === 0) {
console.warn('Invalid screen name: empty or null');
return;
}
if (screenName.length > 100) {
console.warn(`Screen name too long: ${screenName.length} characters`);
}
// Detect rapid duplicate tagging
const now = Date.now();
const lastTagged = this.duplicateDetector.get(screenName) || 0;
if (now - lastTagged < 100) {
// Less than 100ms
console.warn(`Rapid duplicate screen tagging: ${screenName}`);
}
this.duplicateDetector.set(screenName, now);
// Track for analytics
this.taggedScreens.add(screenName);
// Actually tag the screen
RNUxcam.tagScreenName(screenName);
if (__DEV__) {
console.log(`Tagged screen: ${screenName}`);
}
}
static getValidationReport() {
return {
totalScreensTagged: this.taggedScreens.size,
screenNames: Array.from(this.taggedScreens),
potentialIssues: this.findPotentialIssues(),
};
}
static findPotentialIssues() {
const issues = [];
this.taggedScreens.forEach((screen) => {
if (screen.includes('Screen') || screen.includes('Component')) {
issues.push(`Technical name detected: ${screen}`);
}
if (/\d+$/.test(screen)) {
issues.push(`Numbered screen name: ${screen}`);
}
});
return issues;
}
}
// Enhanced tagging function
export const tagScreen = (screenName) => {
if (__DEV__) {
ScreenValidator.tagScreen(screenName);
} else {
RNUxcam.tagScreenName(screenName);
}
};
Testing Screen Flow
import { tagScreen } from '../utils/screenValidation';
describe('Screen Flow Testing', () => {
test('Common user journey screens are tagged', () => {
const commonFlow = [
'Home Dashboard',
'Product Catalog',
'Product Details',
'Shopping Cart',
'Checkout Payment',
'Order Confirmation',
];
commonFlow.forEach((screen) => {
expect(() => tagScreen(screen)).not.toThrow();
});
});
test('Screen names follow naming convention', () => {
const validNames = [
'Home Dashboard',
'User Profile Settings',
'Product Details - Electronics',
];
const invalidNames = ['Screen1', 'HomeComponent', 'com.app.ProductView'];
validNames.forEach((name) => {
expect(name).toMatch(/^[A-Z][a-zA-Z\s-]+$/);
});
});
});
7. Integration with Privacy Protection
Preparing for Phase 3
Good screen names enable efficient privacy protection:
// Phase 2: Screen tagging
RNUxcam.tagScreenName('Login Form');
RNUxcam.tagScreenName('Payment Details');
RNUxcam.tagScreenName('User Profile Settings');
// Phase 3: Privacy protection can target these screens
const privacyConfig = {
type: 'blur',
screens: ['Login Form', 'Payment Details', 'User Profile Settings'],
blurRadius: 15,
};
Screen-Based Privacy Rules
// Define sensitive screens for Phase 3
export const SENSITIVE_SCREENS = [
'Login Form',
'Registration Form',
'Payment Details',
'Credit Card Entry',
'Bank Account Setup',
'Personal Information',
'Password Change',
'Security Settings',
];
export const BLUR_SCREENS = [
'User Profile Settings',
'Account Overview',
'Transaction History',
];
// Use in screen tagging
export const tagScreenWithPrivacy = (screenName) => {
RNUxcam.tagScreenName(screenName);
// Log privacy classification for Phase 3 setup
if (__DEV__) {
if (SENSITIVE_SCREENS.includes(screenName)) {
console.log(`🔒 Sensitive screen tagged: ${screenName}`);
} else if (BLUR_SCREENS.includes(screenName)) {
console.log(`🌫️ Blur screen tagged: ${screenName}`);
}
}
};
8. Performance Optimization
Efficient Screen Tagging
import RNUxcam from 'react-native-ux-cam';
// Cache screen names to avoid string operations
const SCREEN_NAMES = Object.freeze({
HOME: 'Home Dashboard',
PRODUCTS: 'Product Catalog',
PRODUCT_DETAIL: 'Product Details',
CART: 'Shopping Cart',
CHECKOUT: 'Checkout Payment',
PROFILE: 'User Profile',
});
// Batch screen transitions for rapid navigation
class ScreenTaggingBatch {
static pendingTag = null;
static batchTimeout = null;
static tagScreen(screenName) {
// Cancel any pending tag
if (this.batchTimeout) {
clearTimeout(this.batchTimeout);
}
this.pendingTag = screenName;
// Batch rapid screen changes
this.batchTimeout = setTimeout(() => {
if (this.pendingTag) {
RNUxcam.tagScreenName(this.pendingTag);
this.pendingTag = null;
}
}, 50); // 50ms debounce
}
}
export { SCREEN_NAMES, ScreenTaggingBatch };
9. Troubleshooting Common Issues
Screen Names Not Appearing
Issue: Sessions show no screen names or generic names Solutions:
// Verify tagging is called
const debugTagScreen = (screenName) => {
console.log(`Attempting to tag: ${screenName}`);
RNUxcam.tagScreenName(screenName);
// Verify after delay
setTimeout(() => {
console.log('Screen tagging completed');
}, 100);
};
Duplicate Screen Tags
Issue: Same screen tagged multiple times with 0 duration Solutions:
// Use focus/blur pattern correctly
useFocusEffect(
React.useCallback(() => {
RNUxcam.tagScreenName('Home Dashboard');
// Don't tag in cleanup
// return () => { /* cleanup */ };
}, [])
);
Navigation Timing Issues
Issue: Screen tagged before navigation completes Solutions:
// Add small delay for navigation completion
useFocusEffect(
React.useCallback(() => {
const timer = setTimeout(() => {
RNUxcam.tagScreenName('Product Details');
}, 50);
return () => clearTimeout(timer);
}, [])
);
Success Criteria ✅
Before proceeding to Phase 3, verify:
- All major user flows have meaningful screen names
- No screens show 0-second duration consistently
- Screen names are human-readable and descriptive
- Navigation between screens creates logical flow
- Modal and overlay screens are properly tagged
- WebView screens have appropriate names
- No duplicate rapid screen tagging occurs
Next Steps
🎯 Screen Analytics Complete! Your React Native app now provides meaningful screen-level insights.
Continue to Phase 3: Privacy Protection to implement GDPR/CCPA compliant data collection using the screen names you've established.
What You'll Build Next:
- Screen-based privacy rules using your tagged screen names
- Form field occlusion for sensitive data
- WebView privacy protection
- Compliance verification procedures
Benefits of Your Screen Tagging:
- Efficient Privacy Rules: Use screen names for targeted occlusion
- Better Analytics: Heat maps and funnels now have meaningful labels
- User Journey Insights: Clear understanding of user flow patterns
- Performance Tracking: Screen-level engagement and timing metrics
Need Help? Check our Troubleshooting Guide or contact [email protected].
Updated 20 days ago