Bug Report Form That Auto-Captures Browser & OS
Build a bug report form that silently captures the reporter’s browser, operating system, screen resolution, viewport size, page URL, and color scheme preference using JavaScript. The person filing the bug only sees two fields: a description textarea and an optional screenshot upload. All technical context is collected automatically and sent to Forminit as structured, filterable form data. Works on any website, any framework, no backend server required.
What is a bug report form with automatic environment capture?
A bug report form with automatic environment capture is a form that collects technical context (browser name, OS, screen size, current URL) without asking the reporter to provide it manually. The reporter describes the bug and optionally attaches a screenshot. Everything else is detected by JavaScript and submitted as hidden fields.
This matters because manual environment reporting fails consistently. Asking users to paste their browser version or operating system produces incomplete, inaccurate, or missing data. Most users do not know how to find their browser version. The ones who do often copy it wrong. Tools like GitHub Issues, Sentry, and BugHerd recognized this years ago and started capturing environment data automatically. The same approach works for any website with a simple form and a few lines of JavaScript.
The result is bug reports that are immediately actionable. Every submission includes the exact browser, OS, screen dimensions, and page URL, so you can reproduce the issue without a follow-up conversation.
Why use Forminit for a bug report form?
Most approaches to collecting bug reports involve tradeoffs that do not make sense for straightforward use cases.
- Sentry and similar tools are overkill for user-reported bugs. Error monitoring platforms capture crashes and exceptions automatically, but they do not handle the case where a user wants to describe a visual bug, a confusing interaction, or a broken layout. A bug report form collects what automated tools cannot: what the user expected versus what they saw.
- Google Forms and Typeform cannot capture hidden browser data. Form builders have no mechanism for running JavaScript to detect the browser or OS and injecting values into hidden fields. You would need to ask users to self-report their environment, which defeats the purpose.
- Custom backends require server code and a database. Building a
/api/bug-reportendpoint, storing submissions, sending notifications, and handling file uploads is a full backend project for what should be a simple form. Forminit handles all of this with a single API endpoint.
Forminit’s select block type is particularly useful here. Technical fields like browser name and OS become filterable columns in your submission inbox. You can filter all bug reports from Safari users, or all reports from mobile viewports, without parsing text.
What you will build
- A bug report form with automatic browser and OS detection
- Hidden fields for screen resolution, viewport size, page URL, and color scheme
- A visible description textarea for the reporter
- An optional screenshot file upload
- Submission via the Forminit SDK with a fetch fallback
- Works on any static site, CMS, or web application without a backend server
Prerequisites
- A free Forminit account
- A form created in your Forminit dashboard with the Form ID copied
Form blocks reference
The form uses eight Forminit form blocks. Six are hidden fields populated by JavaScript, two are visible to the reporter.
| Block type | Field name | What it captures |
|---|---|---|
| Select | fi-select-browser | Browser name and version (parsed from navigator.userAgentData or navigator.userAgent) |
| Select | fi-select-os | Operating system (parsed from navigator.userAgentData or navigator.userAgent) |
| Select | fi-select-screenResolution | Screen width x height via window.screen |
| Select | fi-select-viewportSize | Viewport width x height via window.innerWidth and window.innerHeight |
| Select | fi-select-pageUrl | Current page URL via window.location.href |
| Select | fi-select-colorScheme | Light or dark mode via window.matchMedia |
| Text | fi-text-description | Bug description written by the reporter |
| File | fi-file-screenshot | Optional screenshot upload |
All six technical fields use the select block type so they appear as filterable columns in the Forminit inbox. This lets you sort and filter bug reports by browser, OS, viewport size, or any combination without writing queries or exporting to a spreadsheet.
Step 1: Create the HTML markup
Replace YOUR_FORM_ID with the Form ID from your Forminit dashboard.
<div id="bug-report" class="bug-report-widget">
<h2 class="bug-report-title">Report a Bug</h2>
<p class="bug-report-subtitle">Describe what went wrong. Browser and device info will be captured automatically.</p>
<form id="bug-report-form">
<!-- Hidden fields populated by JavaScript -->
<input type="hidden" name="fi-select-browser" id="bug-browser" value="" />
<input type="hidden" name="fi-select-os" id="bug-os" value="" />
<input type="hidden" name="fi-select-screenResolution" id="bug-screen" value="" />
<input type="hidden" name="fi-select-viewportSize" id="bug-viewport" value="" />
<input type="hidden" name="fi-select-pageUrl" id="bug-page-url" value="" />
<input type="hidden" name="fi-select-colorScheme" id="bug-color-scheme" value="" />
<!-- Visible fields -->
<div class="bug-report-field">
<label for="bug-description">What happened?</label>
<textarea
name="fi-text-description"
id="bug-description"
rows="5"
required
placeholder="Describe the bug: what you expected, what actually happened, and the steps to reproduce it..."
></textarea>
</div>
<div class="bug-report-field">
<label for="bug-screenshot">Screenshot (optional)</label>
<input
type="file"
name="fi-file-screenshot"
id="bug-screenshot"
accept="image/png, image/jpeg, image/gif, image/webp"
/>
</div>
<button type="submit" class="bug-report-submit" id="bug-submit-btn">Submit bug report</button>
</form>
<div id="bug-report-thanks" class="bug-report-thanks" hidden>
<p>Bug report submitted. Thank you.</p>
</div>
</div>
The six hidden inputs use the fi-select- prefix so each value becomes a filterable column in your Forminit inbox. The fi-file-screenshot field uses Forminit’s native file upload handling, which supports files up to 25 MB with 50+ MIME types.
Step 2: Add the CSS
Minimal, clean styling that works in both light and dark contexts.
.bug-report-widget {
max-width: 560px;
margin: 2rem auto;
padding: 1.5rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
background: #ffffff;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
@media (prefers-color-scheme: dark) {
.bug-report-widget {
background: #1a1a2e;
border-color: #2d2d44;
color: #e2e8f0;
}
}
.bug-report-title {
margin: 0 0 0.25rem 0;
font-size: 1.125rem;
font-weight: 600;
}
.bug-report-subtitle {
margin: 0 0 1.25rem 0;
font-size: 0.875rem;
color: #64748b;
}
@media (prefers-color-scheme: dark) {
.bug-report-subtitle { color: #94a3b8; }
}
.bug-report-field {
margin-bottom: 1rem;
}
.bug-report-field label {
display: block;
font-size: 0.875rem;
font-weight: 500;
margin-bottom: 0.375rem;
}
.bug-report-field textarea {
width: 100%;
padding: 0.625rem;
border: 1px solid #e2e8f0;
border-radius: 6px;
font-size: 0.875rem;
font-family: inherit;
resize: vertical;
background: #ffffff;
color: #1e293b;
box-sizing: border-box;
}
@media (prefers-color-scheme: dark) {
.bug-report-field textarea {
background: #16162a;
border-color: #2d2d44;
color: #e2e8f0;
}
}
.bug-report-field input[type="file"] {
font-size: 0.875rem;
}
.bug-report-submit {
padding: 0.625rem 1.25rem;
background: #3b82f6;
color: #ffffff;
border: none;
border-radius: 6px;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: background 0.15s;
}
.bug-report-submit:hover { background: #2563eb; }
.bug-report-submit:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.bug-report-thanks p {
color: #16a34a;
font-weight: 500;
font-size: 0.95rem;
}
Step 3: Add the JavaScript
Include the Forminit SDK and the detection logic. The script detects browser, OS, screen resolution, viewport size, color scheme, and page URL on load, then submits everything to Forminit when the form is submitted.
<script src="https://forminit.com/sdk/v1/forminit.js"></script>
<script>
const forminit = new Forminit();
const FORM_ID = 'YOUR_FORM_ID'; // Replace with your Form ID
const form = document.getElementById('bug-report-form');
const submitBtn = document.getElementById('bug-submit-btn');
const thanksMessage = document.getElementById('bug-report-thanks');
// --- Browser and OS detection ---
async function getBrowserInfo() {
// Prefer navigator.userAgentData (Chromium 90+)
if (navigator.userAgentData) {
try {
const ua = await navigator.userAgentData.getHighEntropyValues([
'fullVersionList', 'platform', 'platformVersion'
]);
let browserName = 'Unknown';
let browserVersion = '';
// Pick the most specific browser brand
const dominated = ['Chromium', 'Not A(Brand', 'Not;A=Brand', 'Not/A)Brand'];
const brands = ua.fullVersionList || navigator.userAgentData.brands || [];
const primary = brands.find(b => !dominated.some(d => b.brand.includes(d)));
if (primary) {
browserName = primary.brand;
browserVersion = primary.version.split('.')[0];
} else if (brands.length > 0) {
browserName = 'Chromium';
const chromium = brands.find(b => b.brand === 'Chromium');
browserVersion = chromium ? chromium.version.split('.')[0] : '';
}
const os = ua.platform || 'Unknown';
const osVersion = ua.platformVersion || '';
return {
browser: browserVersion ? browserName + ' ' + browserVersion : browserName,
os: osVersion ? os + ' ' + osVersion : os
};
} catch (e) {
// Fall through to userAgent parsing
}
}
return parseUserAgent(navigator.userAgent);
}
function parseUserAgent(ua) {
let browser = 'Unknown';
let os = 'Unknown';
// Browser detection
if (ua.includes('Firefox/')) {
const match = ua.match(/Firefox\/(\d+)/);
browser = 'Firefox' + (match ? ' ' + match[1] : '');
} else if (ua.includes('Edg/')) {
const match = ua.match(/Edg\/(\d+)/);
browser = 'Edge' + (match ? ' ' + match[1] : '');
} else if (ua.includes('OPR/') || ua.includes('Opera')) {
const match = ua.match(/OPR\/(\d+)/);
browser = 'Opera' + (match ? ' ' + match[1] : '');
} else if (ua.includes('Safari/') && !ua.includes('Chrome')) {
const match = ua.match(/Version\/(\d+)/);
browser = 'Safari' + (match ? ' ' + match[1] : '');
} else if (ua.includes('Chrome/')) {
const match = ua.match(/Chrome\/(\d+)/);
browser = 'Chrome' + (match ? ' ' + match[1] : '');
}
// OS detection
if (ua.includes('Windows NT')) {
const match = ua.match(/Windows NT ([\d.]+)/);
const versions = { '10.0': '10/11', '6.3': '8.1', '6.2': '8', '6.1': '7' };
os = 'Windows' + (match ? ' ' + (versions[match[1]] || match[1]) : '');
} else if (ua.includes('Mac OS X')) {
const match = ua.match(/Mac OS X ([\d_]+)/);
os = 'macOS' + (match ? ' ' + match[1].replace(/_/g, '.') : '');
} else if (ua.includes('Android')) {
const match = ua.match(/Android ([\d.]+)/);
os = 'Android' + (match ? ' ' + match[1] : '');
} else if (ua.includes('iPhone') || ua.includes('iPad')) {
const match = ua.match(/OS ([\d_]+)/);
os = 'iOS' + (match ? ' ' + match[1].replace(/_/g, '.') : '');
} else if (ua.includes('Linux')) {
os = 'Linux';
} else if (ua.includes('CrOS')) {
os = 'ChromeOS';
}
return { browser, os };
}
// --- Populate hidden fields ---
async function populateEnvironment() {
const { browser, os } = await getBrowserInfo();
document.getElementById('bug-browser').value = browser;
document.getElementById('bug-os').value = os;
document.getElementById('bug-screen').value = window.screen.width + 'x' + window.screen.height;
document.getElementById('bug-viewport').value = window.innerWidth + 'x' + window.innerHeight;
document.getElementById('bug-page-url').value = window.location.href;
document.getElementById('bug-color-scheme').value =
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
populateEnvironment();
// --- Form submission ---
form.addEventListener('submit', async (e) => {
e.preventDefault();
submitBtn.disabled = true;
submitBtn.textContent = 'Submitting...';
const formData = new FormData(form);
const { error } = await forminit.submit(FORM_ID, formData);
if (error) {
console.error('Bug report submission failed:', error.message);
submitBtn.disabled = false;
submitBtn.textContent = 'Submit bug report';
return;
}
form.hidden = true;
thanksMessage.hidden = false;
});
</script>
When the page loads, populateEnvironment() detects the browser, OS, screen resolution, viewport size, page URL, and color scheme, then writes each value into its corresponding hidden input. When the reporter submits the form, all six hidden values are included alongside the description and optional screenshot. The submit button disables during submission to prevent duplicate reports.
How the browser detection works
The script uses two approaches to detect browser and OS information, choosing the best one available.
navigator.userAgentData (modern approach). Chromium-based browsers (Chrome, Edge, Opera, Brave) version 90 and later support the User-Agent Client Hints API. The getHighEntropyValues() method returns structured data including the browser brand, full version, platform name, and platform version. This is more reliable than parsing a user agent string because the browser provides the data in a machine-readable format. The script filters out generic brand strings like “Chromium” and “Not A(Brand” to find the actual browser name.
navigator.userAgent (fallback). Firefox, Safari, and older browsers do not support userAgentData. For these, the script parses the user agent string with simple regex patterns. This is less precise (Windows 10 and 11 share the same NT version string, for example) but captures enough detail for bug triage. The parsing covers Chrome, Firefox, Safari, Edge, Opera, Windows, macOS, iOS, Android, Linux, and ChromeOS.
The detection runs asynchronously because getHighEntropyValues() returns a Promise. The script awaits the result before populating hidden fields, ensuring values are set before the form can be submitted.
How to add this form to your website
The form works as a standalone page or embedded in an existing layout. Three common placement patterns:
Dedicated bug report page. Create a /report-bug or /feedback page and add the form HTML, CSS, and JavaScript directly. Link to it from your site header, footer, or help menu.
Modal or slide-out panel. Wrap the form in a modal dialog or slide-out drawer triggered by a “Report a bug” button. This keeps the form accessible from any page without navigating away.
Help or support page. Add the form to an existing support page alongside other contact options. The automatic environment capture means the bug report section needs no extra instructions.
Tips for collecting useful bug reports
Keep the form short. Two visible fields (description and screenshot) are enough. Every additional required field reduces completion rates. The environment data is captured silently, so there is nothing for the reporter to look up or paste.
Make the screenshot optional. Not every bug is visual, and requiring a screenshot creates friction on mobile devices. The reporter should be able to submit a text-only report and attach a screenshot when it helps.
Use the filtered inbox to triage by browser. Because browser, OS, and viewport size are stored as select block fields, you can filter your Forminit inbox to see all bug reports from Safari users, or all reports from viewports narrower than 768px. This helps identify browser-specific or responsive layout issues quickly.
Set up email notifications for new reports. Turn on Forminit’s email notifications so your team receives an alert for every new bug report. For higher-volume forms, forward submissions to Slack or Discord via Forminit’s native integrations.
Use webhooks to forward reports to your issue tracker. Forminit’s webhook integration can POST submission data to any URL. Connect it to your project management tool (Linear, Jira, GitHub Issues) so bug reports create tickets automatically.
Frequently asked questions
What browser information should a bug report form capture?
At minimum, capture the browser name and version, operating system, and the page URL where the bug occurred. Screen resolution and viewport size are useful for layout bugs. Color scheme preference (light or dark) helps reproduce theme-related issues. The form in this guide captures all six automatically.
Does navigator.userAgent still work in modern browsers?
Yes. All major browsers still expose navigator.userAgent, though some have started reducing the detail it contains (Chrome’s User-Agent Reduction initiative, for example). The script in this guide uses navigator.userAgentData when available for higher accuracy and falls back to navigator.userAgent parsing for Firefox, Safari, and older browsers. Both approaches produce usable results for bug triage.
Can I add this bug report form to a static site?
Yes. The form uses client-side JavaScript and submits to the Forminit API, so no server is required. It works on GitHub Pages, Netlify, Vercel, Cloudflare Pages, or any host that serves HTML files. Include the HTML, CSS, and JavaScript on any page and replace YOUR_FORM_ID with your Forminit Form ID.
How do I filter bug reports by browser or OS?
In the Forminit submission inbox, use the column filters on the browser or os fields. Because these are submitted as fi-select- block types, they appear as structured, filterable columns rather than freeform text. You can filter to see only Safari submissions, only Windows reports, or any combination.
Do I need a backend server to handle bug reports?
No. Forminit is a headless form backend that handles submission processing, file storage, notifications, and webhooks. You submit form data from your frontend code to the Forminit API. Submissions appear in your Forminit dashboard where you can filter, search, export, and forward them.
Can reporters attach files to the bug report?
Yes. The fi-file-screenshot field uses Forminit’s native file upload handling. Forminit supports files up to 25 MB per submission with 50+ MIME types including PNG, JPEG, GIF, WebP, PDF, and more. Uploaded files are stored securely and accessible via direct download URLs in the submission inbox. See the file uploads guide for details.
Further reading
- What Is a Headless Form Backend? - How Forminit works under the hood
- Form Blocks Reference - All field types and naming conventions
- File Uploads Guide - Adding file upload fields to any form
- Forminit HTML Integration Guide - Complete HTML setup guide
- Webhooks Guide - Forward submissions to external services
- Add a “Was This Page Helpful?” Feedback Widget - Another recipe for collecting user feedback with Forminit