diff --git a/pkg/commands/run.go b/pkg/commands/run.go
index 31a4e791..271fffe9 100644
--- a/pkg/commands/run.go
+++ b/pkg/commands/run.go
@@ -432,6 +432,8 @@ func (e *Executor) createPrinter() (printers.Printer, error) {
 		p = printers.NewCheckstyle()
 	case config.OutFormatCodeClimate:
 		p = printers.NewCodeClimate()
+	case config.OutFormatHTML:
+		p = printers.NewHTML()
 	case config.OutFormatJunitXML:
 		p = printers.NewJunitXML()
 	case config.OutFormatGithubActions:
diff --git a/pkg/config/output.go b/pkg/config/output.go
index c95d58fb..d67f110f 100644
--- a/pkg/config/output.go
+++ b/pkg/config/output.go
@@ -7,6 +7,7 @@ const (
 	OutFormatTab               = "tab"
 	OutFormatCheckstyle        = "checkstyle"
 	OutFormatCodeClimate       = "code-climate"
+	OutFormatHTML              = "html"
 	OutFormatJunitXML          = "junit-xml"
 	OutFormatGithubActions     = "github-actions"
 )
@@ -18,6 +19,7 @@ var OutFormats = []string{
 	OutFormatTab,
 	OutFormatCheckstyle,
 	OutFormatCodeClimate,
+	OutFormatHTML,
 	OutFormatJunitXML,
 	OutFormatGithubActions,
 }
diff --git a/pkg/printers/html.go b/pkg/printers/html.go
new file mode 100644
index 00000000..65ab753b
--- /dev/null
+++ b/pkg/printers/html.go
@@ -0,0 +1,155 @@
+package printers
+
+import (
+	"context"
+	"fmt"
+	"html/template"
+	"strings"
+
+	"github.com/golangci/golangci-lint/pkg/logutils"
+	"github.com/golangci/golangci-lint/pkg/result"
+)
+
+const templateContent = `<!doctype html>
+<html lang="en">
+<head>
+    <meta charset="utf-8">
+    <title>golangci-lint</title>
+    <link rel="shortcut icon" type="image/png" href="https://golangci-lint.run/favicon-32x32.png">
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.2/css/bulma.min.css"
+          integrity="sha512-byErQdWdTqREz6DLAA9pCnLbdoGGhXfU6gm1c8bkf7F51JVmUBlayGe2A31VpXWQP+eiJ3ilTAZHCR3vmMyybA=="
+          crossorigin="anonymous" referrerpolicy="no-referrer"/>
+    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css"
+          integrity="sha512-kZqGbhf9JTB4bVJ0G8HCkqmaPcRgo88F0dneK30yku5Y/dep7CZfCnNml2Je/sY4lBoqoksXz4PtVXS4GHSUzQ=="
+          crossorigin="anonymous" referrerpolicy="no-referrer"/>
+    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js"
+            integrity="sha512-s+tOYYcC3Jybgr9mVsdAxsRYlGNq4mlAurOrfNuGMQ/SCofNPu92tjE7YRZCsdEtWL1yGkqk15fU/ark206YTg=="
+            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/languages/go.min.js"
+            integrity="sha512-+UYV2NyyynWEQcZ4sMTKmeppyV331gqvMOGZ61/dqc89Tn1H40lF05ACd03RSD9EWwGutNwKj256mIR8waEJBQ=="
+            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"
+            integrity="sha512-qlzIeUtTg7eBpmEaS12NZgxz52YYZVF5myj89mjJEesBd/oE9UPsYOX2QAXzvOAZYEvQohKdcY8zKE02ifXDmA=="
+            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+    <script type="text/javascript"
+            src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"
+            integrity="sha512-9jGNr5Piwe8nzLLYTk8QrEMPfjGU0px80GYzKZUxi7lmCfrBjtyCc1V5kkS5vxVwwIB7Qpzc7UxLiQxfAN30dw=="
+            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"
+            integrity="sha512-kp7YHLxuJDJcOzStgd6vtpxr4ZU9kjn77e6dBsivSz+pUuAuMlE2UTdKB7jjsWT84qbS8kdCWHPETnP/ctrFsA=="
+            crossorigin="anonymous" referrerpolicy="no-referrer"></script>
+</head>
+<body>
+<section class="section">
+    <div class="container">
+        <div id="content"></div>
+    </div>
+</section>
+<script>
+    const data = {{ . }};
+</script>
+<script type="text/babel">
+  class Highlight extends React.Component {
+    componentDidMount() {
+      hljs.highlightElement(ReactDOM.findDOMNode(this));
+    }
+
+    render() {
+      return <pre className="go"><code>{this.props.code}</code></pre>;
+    }
+  }
+
+  class Issue extends React.Component {
+    render() {
+      return (
+        <div className="issue box">
+          <div>
+            <div className="columns">
+              <div className="column is-four-fifths">
+                <h5 className="title is-5 has-text-danger-dark">{this.props.data.Title}</h5>
+              </div>
+              <div className="column is-one-fifth">
+                <h6 className="title is-6">{this.props.data.Linter}</h6>
+              </div>
+            </div>
+            <strong>{this.props.data.Pos}</strong>
+          </div>
+          <div className="highlight">
+            <Highlight code={this.props.data.Code}/>
+          </div>
+        </div>
+      );
+    }
+  }
+
+  class Issues extends React.Component {
+    render() {
+      if (!this.props.data.Issues || this.props.data.Issues.length === 0) {
+        return (
+          <div>
+            <div className="notification">
+              No issues found!
+            </div>
+          </div>
+        );
+      }
+
+      return (
+        <div className="issues">
+          {this.props.data.Issues.map(issue => (<Issue data={issue}/>))}
+        </div>
+      );
+    }
+  }
+
+  ReactDOM.render(
+    <div className="content">
+      <div className="columns is-centered">
+        <div className="column is-three-quarters">
+          <Issues data={data}/>
+        </div>
+      </div>
+    </div>,
+    document.getElementById("content")
+  );
+</script>
+</body>
+</html>`
+
+type htmlIssue struct {
+	Title  string
+	Pos    string
+	Linter string
+	Code   string
+}
+
+type HTML struct{}
+
+func NewHTML() *HTML {
+	return &HTML{}
+}
+
+func (h HTML) Print(_ context.Context, issues []result.Issue) error {
+	var htmlIssues []htmlIssue
+
+	for i := range issues {
+		pos := fmt.Sprintf("%s:%d", issues[i].FilePath(), issues[i].Line())
+		if issues[i].Pos.Column != 0 {
+			pos += fmt.Sprintf(":%d", issues[i].Pos.Column)
+		}
+
+		htmlIssues = append(htmlIssues, htmlIssue{
+			Title:  strings.TrimSpace(issues[i].Text),
+			Pos:    pos,
+			Linter: issues[i].FromLinter,
+			Code:   strings.Join(issues[i].SourceLines, "\n"),
+		})
+	}
+
+	t, err := template.New("golangci-lint").Parse(templateContent)
+	if err != nil {
+		return err
+	}
+
+	return t.Execute(logutils.StdOut, struct{ Issues []htmlIssue }{Issues: htmlIssues})
+}