PostHog provides a powerful analytics and experimentation platform, including built-in surveys for collecting user feedback. However, in real-world applications especially dashboards, internal tools, or B2B SaaS products the default survey UI is not always flexible enough.
In this article, I’ll walk through how I built a fully custom in-app survey UI on top of PostHog, while still using PostHog as the data and analytics layer.
This approach allowed me to keep full control over UX, layout, and behavior without introducing an additional backend or feedback system.
Why Go Beyond the Default Survey UI?
PostHog’s built-in surveys are great for quick setups, but I ran into a few limitations and I would like to edit the pop-up.
Limited control over positioning and layout
UI didn’t fully match with my applicaiton
Needed a lightweight, non-intrusive CES-style survey
Wanted tighter control over when and how the survey appears
Instead of replacing PostHog, I decided to treat it as a survey engine, not a UI framework.
Architecture
The idea is simple:
PostHog Survey
↓
onSurveysLoaded()
↓
getActiveMatchingSurveys()
↓
Custom Angular UI
↓
posthog.capture()
PostHog decides when a survey should be shown.
My application decides how it should look and behave.
Listening for Active Surveys
One important lesson: calling getActiveMatchingSurveys() too early often returns an empty array.
The correct approach is to wait for surveys to finish loading.
posthog.onSurveysLoaded(() => {
posthog.getActiveMatchingSurveys((surveys) => {
if (!surveys.length) return;
const survey = surveys[0];
this.surveyId = survey.id;
this.questionId = survey.questions[0]?.id;
this.question = survey.questions[0]?.question;
});
});
Why this matters
Without onSurveysLoaded, surveys may not be available yet
Ad blockers or strict CSP rules can silently block surveys
Defensive checks prevent runtime errors
Building a Lightweight Custom UI
Rather than using modals or full-screen components, I chose a small fixed-position card with a basic css, that feels native.
.survey {
position: fixed;
bottom: 24px;
right: 24px;
max-width: 360px;
padding: 16px;
border-radius: 4px;
border: 1px solid #ddd;
background: #fff;
font-size: 14px;
z-index: 1000;
}
@media (max-width: 768px) {
.survey {
right: 16px;
left: 16px;
}
}
Design goals:
- Minimal color usage
- Mobile-friendly
- Easy to dismiss
No disruption to the main workflow
Submitting Survey Responses
When the user submits a response, the survey is sent back to PostHog as a custom event.
posthog.capture('custom_survey_submitted', {
survey_id: this.surveyId,
question_id: this.questionId,
value: selectedValue,
});
When Does a Custom Survey Make Sense?
Custom surveys are especially useful when:
You need design-system consistency
You’re building internal tools or admin panels
Feedback should feel contextual, not disruptive
You want analytics-grade data, not just form responses
PostHog excels as the decision and data layer,custom UI lets your product own the experience.
If you want, you can visit posthog website,they doing lots of great things.
Useful Links:
https://posthog.com/
https://github.com/PostHog/posthog
https://www.npmjs.com/package/posthog-js


Top comments (0)