Guide · updated 4 October 2026

GitHub issue template for bug reports: a form you can copy

A blank GitHub issue gets you a one-line bug report with no steps and no build number. A template asks for the missing parts before the issue is saved. Here is how they work, and three files you can copy.

How GitHub issue templates work

Templates live in one folder on the repository's default branch: .github/ISSUE_TEMPLATE/. Each file in it becomes a choice on the New issue page. When someone picks one, the issue starts from that template instead of an empty box.

There are two kinds of template:

  • Markdown templates (.md). A block of text that pre-fills the issue body. Front matter at the top sets the name, title and labels. People can delete any of it.
  • Issue forms (.yml). A real form with text boxes, dropdowns and required fields. GitHub will not create the issue until the required fields are filled. The answers are written into the issue as headed sections.

A third file, config.yml, sits in the same folder. It controls the chooser itself: blank_issues_enabled decides whether people can still open an issue with no template, and contact_links adds links that send questions and security reports somewhere else.

The folder ends up like this:

  • .github/ISSUE_TEMPLATE/bug_report.yml — the form
  • .github/ISSUE_TEMPLATE/config.yml — the chooser settings

The bug report issue form

Use a form for bug reports. Required fields are the only reliable way to get steps and a build number. This one asks for what happened, steps, the expected result, platform, version, browser or device, severity, and screenshots. Save it as .github/ISSUE_TEMPLATE/bug_report.yml.

bug_report.yml
name: Bug report
description: Something in the app does not work as it should.
title: "[Bug]: "
labels: ["bug"]
body:
  - type: markdown
    attributes:
      value: |
        Thanks for reporting this. One bug per report, please.
        Search the open issues first in case it is already known.
  - type: textarea
    id: what-happened
    attributes:
      label: What happened?
      description: Describe what you saw, in your own words.
      placeholder: The Pay button spins forever and the order is never placed.
    validations:
      required: true
  - type: textarea
    id: steps
    attributes:
      label: Steps to reproduce
      description: Numbered steps somebody else can follow from the start.
      placeholder: |
        1. Sign in as a customer with a saved card
        2. Add any item to the cart
        3. Tap Checkout, then Pay
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: Expected result
      description: What should have happened instead?
      placeholder: The order is placed and a receipt screen opens.
    validations:
      required: true
  - type: dropdown
    id: platform
    attributes:
      label: Platform
      description: Where did it happen? Pick every one you saw it on.
      multiple: true
      options:
        - Web
        - iOS
        - Android
        - Desktop
    validations:
      required: true
  - type: input
    id: version
    attributes:
      label: App version or build
      description: Found in Settings > About, or the TestFlight / Play build number.
      placeholder: 2.4.1 (build 318)
    validations:
      required: true
  - type: input
    id: device
    attributes:
      label: Browser or device
      placeholder: Chrome 129 on macOS, or iPhone 15 on iOS 18.1
    validations:
      required: false
  - type: dropdown
    id: severity
    attributes:
      label: Severity
      options:
        - Blocker - cannot continue at all
        - Major - a main feature is broken
        - Minor - works, but wrongly
        - Cosmetic - looks wrong only
    validations:
      required: false
  - type: textarea
    id: evidence
    attributes:
      label: Screenshots or recording
      description: Drag images or a short screen recording into this box.
    validations:
      required: false

A few things about the syntax, so your own edits stay valid:

  • Every item under body: has a type: markdown, textarea, input, dropdown or checkboxes.
  • id must be unique, and only letters, numbers, - and _. Markdown blocks do not need one.
  • validations.required: true makes a field mandatory. Markdown blocks cannot be required.
  • multiple: true on a dropdown lets people pick more than one option.
  • Labels in labels: are only applied if they exist in the repository. bug exists by default.
  • A broken form does not show in the chooser. Open the file on GitHub after you push: it shows a preview, or the error.

The Markdown alternative

If you would rather keep it loose, a Markdown template does the same job with no validation. Save it as .github/ISSUE_TEMPLATE/bug_report.md. Use one or the other for bugs, not both, or the chooser shows two bug reports.

bug_report.md
---
name: Bug report
about: Something in the app does not work as it should.
title: "[Bug]: "
labels: bug
assignees: ""
---

## What happened?

## Steps to reproduce
1.
2.
3.

## Expected result

## Platform
Web / iOS / Android / Desktop

## App version or build

## Browser or device

## Severity
Blocker / Major / Minor / Cosmetic

## Screenshots or recording

The chooser: config.yml

Turning off blank issues means every bug arrives through the template. Contact links keep questions out of the bug list. Replace the URLs with your own.

config.yml
blank_issues_enabled: false
contact_links:
  - name: Questions and help
    url: https://github.com/your-org/your-repo/discussions
    about: Ask how something works. Bugs go in a bug report.
  - name: Security problem
    url: https://example.com/security
    about: Please report security issues privately, not in a public issue.

Tips that make the template work

  • Keep required fields few. Require what you cannot fix without: what happened, steps, expected result, platform and build. Make the rest optional. A form with ten required boxes gets abandoned or filled with “n/a”.
  • Always ask for the build or version. “It's broken” on last week's build may already be fixed. Without a build number nobody can tell.
  • Write placeholders as examples. A placeholder that shows a good answer gets better answers than a description of what to write.
  • One bug per issue. Say so at the top of the form. Two bugs in one issue means one gets closed and the other is forgotten.
  • Link the fix. Put Fixes #123 in the pull request description. When it merges into the default branch, GitHub closes issue #123 and links the two.

For what makes a good report in general, whatever tool it is written in, see the bug report template guide.

Where a template stops

A template helps people write a good report. It does nothing after that. When the pull request merges, GitHub closes the issue, and that is the end of it. Nobody tells the tester the fix has landed. Nobody checks the bug is actually gone on the build they were testing. A closed issue means the code changed, not that the bug is fixed.

This matters most when testers are not developers and do not live in GitHub.

How QA Runbook closes the loop

In QA Runbook, testers raise the bug against the check they were running, and it gets a short code like QA-13. Connect the app to its GitHub repository and the rest follows:

  • You can choose to file every new issue in the connected repository automatically. It opens as a GitHub issue with its code in the title and a link back. Or send issues one at a time.
  • A pull request that says Fixes QA-13, or Fixes #8 for the GitHub issue, marks it fixed in QA Runbook when it merges. The code also works in the pull request title or branch name.
  • The tester who raised it is told to check it again. The check shows retest on the platforms it affects, instead of quietly going back to passing.

Developers keep working in GitHub. Testers keep working in the runbook. Both see the same state. Setup takes a few minutes: see Mark issues fixed from GitHub.