Feature Request Forms That Prioritize Themselves
"We need feature X" tells you nothing about priority. Who needs it? How often? What's the impact? A good feature request form extracts this context automatically, scoring each request so you can focus on what actually matters to your users.
The goal isn't just collecting ideas - it's collecting enough context to make decisions. When every request comes with frequency, impact, and user context, your backlog starts prioritizing itself.
Basic Request Information
Start with the essentials: what they want and why. A clear title, category, and description give your team the basics.
const requestSection = form.addSubform('request', {
title: 'Feature Request'
});
requestSection.addRow(row => {
row.addTextbox('title', {
label: 'Feature Title',
isRequired: true,
placeholder: 'Brief, descriptive name for the feature',
maxLength: 100
});
});
requestSection.addRow(row => {
row.addDropdown('category', {
label: 'Category',
isRequired: true,
options: [
{ id: 'ui', name: 'User Interface' },
{ id: 'integration', name: 'Integrations' },
{ id: 'reporting', name: 'Reporting & Analytics' },
{ id: 'automation', name: 'Automation' },
{ id: 'mobile', name: 'Mobile App' },
{ id: 'api', name: 'API & Developer Tools' },
{ id: 'other', name: 'Other' }
]
});
});
requestSection.addRow(row => {
row.addTextarea('description', {
label: 'Description',
isRequired: true,
placeholder: 'What would this feature do? Be as specific as possible.',
rows: 4
});
});Categories help route requests to the right team and reveal patterns. If 40% of requests are "Integrations," that's a signal.
Problem and Workaround
Features are solutions. Understanding the underlying problem often reveals better solutions - or existing features they didn't know about.
requestSection.addRow(row => {
row.addTextarea('problem', {
label: 'What problem does this solve?',
isRequired: true,
placeholder: 'Describe the pain point or limitation you\'re experiencing',
rows: 3
});
});
requestSection.addRow(row => {
row.addTextarea('currentWorkaround', {
label: 'How do you handle this today?',
placeholder: 'Any workarounds or manual processes you use currently',
rows: 2
});
});The workaround field is gold. If someone has a complex manual process, that's a real pain point. If they have no workaround, they're blocked. If the workaround is "use competitor X," you know the stakes.
Pro tip
"What problem does this solve?" catches bad requests early. If someone can't articulate the problem, they're often requesting a solution they imagined rather than something they actually need.
Impact Assessment
This is where self-prioritization happens. Three dimensions of impact create a score that separates "would be nice" from "desperately need."
const impactSection = form.addSubform('impact', {
title: 'Impact Assessment'
});
impactSection.addRow(row => {
row.addRadioButton('frequency', {
label: 'How often would you use this?',
isRequired: true,
options: [
{ id: 'daily', name: 'Daily' },
{ id: 'weekly', name: 'Weekly' },
{ id: 'monthly', name: 'Monthly' },
{ id: 'occasionally', name: 'Occasionally' }
],
orientation: 'horizontal'
});
});
impactSection.addRow(row => {
row.addRadioButton('userImpact', {
label: 'How many users would benefit?',
isRequired: true,
options: [
{ id: 'just-me', name: 'Just me' },
{ id: 'my-team', name: 'My team' },
{ id: 'department', name: 'My department' },
{ id: 'company', name: 'Whole company' }
],
orientation: 'horizontal'
});
});
impactSection.addRow(row => {
row.addRadioButton('businessImpact', {
label: 'Impact on your work',
isRequired: true,
options: [
{ id: 'nice', name: 'Nice to have' },
{ id: 'helpful', name: 'Would help significantly' },
{ id: 'important', name: 'Important for my workflow' },
{ id: 'critical', name: 'Critical - blocking my work' }
],
orientation: 'vertical'
});
});Frequency, user scope, and business impact combine to show true priority. A feature used daily by the whole company that's critical to workflows scores very differently than something one person would use occasionally.
Automatic Priority Scoring
Turn those assessments into a score. Higher scores mean higher priority - no manual triage needed for initial ranking.
const frequency = impactSection.radioButton('frequency');
const userImpact = impactSection.radioButton('userImpact');
const businessImpact = impactSection.radioButton('businessImpact');
const frequencyScores: Record<string, number> = {
'daily': 4, 'weekly': 3, 'monthly': 2, 'occasionally': 1
};
const userScores: Record<string, number> = {
'just-me': 1, 'my-team': 2, 'department': 3, 'company': 4
};
const businessScores: Record<string, number> = {
'nice': 1, 'helpful': 2, 'important': 3, 'critical': 4
};
const priorityScore = form.computedValue(() => {
const freq = frequencyScores[frequency?.value() || ''] || 0;
const users = userScores[userImpact?.value() || ''] || 0;
const biz = businessScores[businessImpact?.value() || ''] || 0;
return freq + users + biz;
});
const getPriorityLabel = (score: number): string => {
if (score >= 10) return 'High Priority';
if (score >= 7) return 'Medium Priority';
if (score >= 4) return 'Low Priority';
return 'Not yet scored';
};
impactSection.addRow(row => {
row.addTextPanel('priorityDisplay', {
label: 'Calculated Priority',
computedValue: () => {
const score = priorityScore();
if (score === 0) return 'Complete the assessment above';
return getPriorityLabel(score) + ' (score: ' + score + '/12)';
}
});
});The score updates as users fill out the impact section. They see their request classified as Low, Medium, or High Priority. This sets expectations and encourages honest assessment - gaming the system is obvious.
See more form examples in our gallery.
Use Cases and Alternatives
Concrete examples help your team understand the request. References to other products show what users expect.
requestSection.addRow(row => {
row.addTextarea('useCase', {
label: 'Example Use Case',
placeholder: 'Walk us through a specific scenario where you\'d use this feature',
rows: 3
});
});
requestSection.addRow(row => {
row.addTextbox('alternatives', {
label: 'Have you seen this elsewhere?',
placeholder: 'Other products that have this feature (optional)'
});
});"I saw this in Notion and it would help me organize projects better" gives your team a reference point and context about expectations.
Time Saved
Time savings quantify impact in a way that resonates with both users and stakeholders. Hours saved per week becomes ROI.
impactSection.addRow(row => {
row.addDropdown('timeSaved', {
label: 'Time this would save you (per week)',
options: [
{ id: 'minimal', name: 'Less than 30 minutes' },
{ id: 'some', name: '30 minutes - 2 hours' },
{ id: 'significant', name: '2-5 hours' },
{ id: 'major', name: 'More than 5 hours' }
]
});
});
const timeSaved = impactSection.dropdown('timeSaved');
impactSection.addRow(row => {
row.addTextPanel('roiHint', {
label: '',
isVisible: () => timeSaved?.value() === 'significant' || timeSaved?.value() === 'major',
computedValue: () => {
const t = timeSaved?.value();
if (t === 'significant') return 'That\'s 100-250+ hours per year!';
if (t === 'major') return 'That\'s 250+ hours per year - huge impact!';
return '';
}
});
});The ROI hint makes large time savings feel concrete. "250+ hours per year" is more compelling than "more than 5 hours per week."
Willingness to Pay
The ultimate signal: would they pay for it? This separates "nice idea" from "take my money."
impactSection.addRow(row => {
row.addRadioButton('payMore', {
label: 'Would you pay more for this feature?',
options: [
{ id: 'no', name: 'No' },
{ id: 'maybe', name: 'Maybe' },
{ id: 'yes', name: 'Yes' },
{ id: 'upgrade', name: 'I\'d upgrade my plan for this' }
],
orientation: 'horizontal'
});
});"I'd upgrade my plan for this" is the strongest signal you can get. Even "maybe" indicates real interest. Mostly "no" responses suggest the feature isn't as important as users claim.
User Context
Who's requesting matters. Power users have different needs than new users. Enterprise customers have different priorities than free tier users.
const contextSection = form.addSubform('context', {
title: 'About You'
});
contextSection.addRow(row => {
row.addDropdown('role', {
label: 'Your Role',
options: [
{ id: 'admin', name: 'Admin / Owner' },
{ id: 'manager', name: 'Manager' },
{ id: 'user', name: 'Regular User' },
{ id: 'developer', name: 'Developer' }
]
});
row.addDropdown('plan', {
label: 'Your Plan',
options: [
{ id: 'free', name: 'Free' },
{ id: 'pro', name: 'Pro' },
{ id: 'business', name: 'Business' },
{ id: 'enterprise', name: 'Enterprise' }
]
});
});
contextSection.addRow(row => {
row.addEmail('email', {
label: 'Email (for follow-up)',
isRequired: true,
placeholder: 'We\'ll notify you when this ships'
});
});
contextSection.addRow(row => {
row.addCheckbox('betaTester', {
label: 'I\'d like to beta test this feature when available'
});
});Role and plan help weight requests appropriately. The beta tester checkbox builds a list of engaged users to involve when you build the feature.
Supporting Materials
Sometimes a mockup or screenshot explains better than words. Give users a place to share visuals.
requestSection.addRow(row => {
row.addTextbox('mockupUrl', {
label: 'Mockup or Screenshot URL',
placeholder: 'Link to any visuals that help explain your idea'
});
});
requestSection.addRow(row => {
row.addTextarea('additionalNotes', {
label: 'Anything else?',
placeholder: 'Additional context, constraints, or ideas',
rows: 2
});
});URL fields for mockups keep the form simple while allowing rich context for those who have it.
What to Do With Request Data
The form creates structured data. Here's how to use it:
Sort by score: Start with highest-priority requests. The scoring system surfaces what matters most.
Group by category: See which areas need attention. Heavy clustering in one category might justify dedicated investment.
Filter by plan: Enterprise requests from enterprise customers might warrant different treatment than free-tier suggestions.
Contact beta testers: When you build something, reach out to users who requested it. They'll validate, test, and champion the feature.
Close the loop: Email requesters when their feature ships. This builds loyalty and shows you listen.
Keeping It Honest
Users will try to game priority scores. A few safeguards help:
Show the score: Transparency discourages obvious exaggeration. Claiming "critical impact" when it's clearly not looks foolish.
Require problem description: Forcing articulation of the problem grounds requests in reality.
Cross-reference context: A free-tier user claiming "whole company" impact is probably exaggerating. Plan and role provide sanity checks.
Aggregate over individual: One high-priority request means little. Ten similar requests reveal a real need.
Common Questions
Should I let users vote on existing requests?
Voting helps validate demand but requires a separate system - a public roadmap or feature board. The form collects individual requests with context. Voting aggregates demand across requests. Both are useful for different purposes.
How do I handle duplicate requests?
Duplicates are signal, not noise. If 50 people request the same thing, that's valuable data. You can dedupe in your backlog while preserving the count and context from each submission.
Should I show the priority score to users?
Yes. Transparency sets expectations and encourages honest assessment. If someone inflates their impact, seeing 'Low Priority' might prompt reconsideration. It also helps users understand why their request might not be built immediately.
How do I handle feature requests from non-customers?
Prospects requesting features are valuable - they're telling you what would make them convert. Include a 'relationship' field (customer, prospect, churned) and weight accordingly. A prospect's 'would upgrade for this' is different from a customer's.