In the previous Feed Component System chapter, we learned how to display lists of data.
But sometimes, we need to show something specific: a feature announcement.
We could put all this logic in our main parent component, but that would create a mess of if/else statements. Instead, we use Conditional Feature Notices.
Imagine if the main LogoV2 component had to know the rules for every feature:
// BAD EXAMPLE: The parent knows too much
function LogoV2() {
return (
<Box>
{/* Logic cluttering the layout */}
{user.hasVoice && user.seenVoice < 3 && <VoiceBanner />}
{config.channelsEnabled && !config.policyBlocked && <ChannelsBanner />}
{/* ... 10 more checks ... */}
</Box>
)
}
This is hard to read and hard to maintain.
We solve this by moving the logic inside the component. The component asks itself: "Should I exist?"
If the answer is No, the component returns null (rendering nothing). The parent doesn't need to know why it's hidden; it just attempts to render it.
// GOOD EXAMPLE: The parent is clean
function LogoV2() {
return (
<Box>
<VoiceModeNotice />
<ChannelsNotice />
</Box>
)
}
To build a Conditional Feature Notice, we follow three steps:
Let's look at VoiceModeNotice.tsx to see this pattern in action.
The most important part of these components is the Early Return. If a condition isn't met, we stop immediately.
// From VoiceModeNotice.tsx
export function VoiceModeNotice() {
// 1. Feature Flag Check
const isFeatureOn = feature('VOICE_MODE');
if (!isFeatureOn) {
return null; // Render nothing!
}
return <VoiceModeNoticeInner />;
}
<VoiceModeNotice />. If the feature flag VOICE_MODE is off, the component effectively disappears.
Sometimes, simply checking a flag isn't enough. We might need to check multiple things. Let's look inside VoiceModeNoticeInner.
// From VoiceModeNotice.tsx (Simplified)
function VoiceModeNoticeInner() {
// Check settings, config, and counts all at once
const [shouldShow] = useState(() => {
return (
isVoiceModeEnabled() && // Is it working?
!settings.voiceEnabled && // Is it not yet turned on?
seenCount < MAX_SHOW_COUNT // Have we annoyed them yet?
);
});
if (!shouldShow) return null;
return <Box><Text>Voice mode available!</Text></Box>;
}
shouldShow. If any rule fails (e.g., the user has already enabled voice), the notice hides itself.
Sometimes a notice isn't just "On" or "Off". It might need to show a warning.
The ChannelsNotice.tsx component handles three different scenarios:
// From ChannelsNotice.tsx (Simplified)
export function ChannelsNotice() {
const { disabled, noAuth, policyBlocked } = useChannelState();
if (disabled) {
return <Text color="error">Channels are not available</Text>;
}
if (noAuth) {
return <Text dimColor>Requires authentication</Text>;
}
// ... if everything is fine, show nothing or a success message ...
return null;
}
How does the component decide when to stop showing itself (e.g., "Show this 3 times")?
It uses a combination of Reading the global config and Writing back to it.
Here is the actual code pattern used to count views, found in VoiceModeNotice.tsx.
// From VoiceModeNotice.tsx
useEffect(() => {
if (!show) return; // Don't count if we didn't show it
// Update the global config file
saveGlobalConfig(prev => {
return {
...prev,
voiceNoticeSeenCount: (prev.voiceNoticeSeenCount ?? 0) + 1
};
});
}, [show]);
1. We use useEffect to run code after the component renders.
2. We call saveGlobalConfig.
3. We take the previous count and add +1.
4. Next time the app starts, the logic in Step 2 will read this new number.
Not all logic is hard-coded. Sometimes we need to fetch a message from a server (like a remote configuration).
The EmergencyTip.tsx component uses a "Dynamic Config" service.
// From EmergencyTip.tsx
function getTipOfFeed() {
// Fetch from GrowthBook (feature flagging service)
return getDynamicConfig('tengu-top-of-feed-tip', DEFAULT_TIP);
}
export function EmergencyTip() {
const tip = useMemo(getTipOfFeed, []);
// If the tip string is empty, hide the component
if (!tip.text) return null;
return <Text color={tip.color}>{tip.text}</Text>;
}
null.Conditional Feature Notices keep our layout clean by offloading the "brain work" to the components themselves.
null (invisible) if conditions aren't met.Speaking of "nagging the user," counting how many times we've shown a notice is a pattern we use frequently. In the next chapter, we will formalize this into a reusable system.
Next Chapter: Upsell Impression Management
Generated by Code IQ