diff --git a/cmd/flux/get.go b/cmd/flux/get.go
index f52fd496a09934d05168b01fa8997bcd11ef32e9..29fa1c2d20260a25a62cbd44b7bb4c3c1ebbd00e 100644
--- a/cmd/flux/get.go
+++ b/cmd/flux/get.go
@@ -33,6 +33,7 @@ import (
 	"github.com/fluxcd/pkg/apis/meta"
 
 	"github.com/fluxcd/flux2/internal/utils"
+	"github.com/fluxcd/flux2/pkg/printers"
 )
 
 type deriveType func(runtime.Object) (summarisable, error)
@@ -177,7 +178,10 @@ func (get getCommand) run(cmd *cobra.Command, args []string) error {
 		return err
 	}
 
-	utils.PrintTable(cmd.OutOrStdout(), header, rows)
+	err = printers.TablePrinter(header).Print(cmd.OutOrStdout(), rows)
+	if err != nil {
+		return err
+	}
 
 	if getAll {
 		fmt.Println()
@@ -242,10 +246,16 @@ func watchUntil(ctx context.Context, w watch.Interface, get *getCommand) (bool,
 			return false, err
 		}
 		if firstIteration {
-			utils.PrintTable(os.Stdout, header, rows)
+			err = printers.TablePrinter(header).Print(os.Stdout, rows)
+			if err != nil {
+				return false, err
+			}
 			firstIteration = false
 		} else {
-			utils.PrintTable(os.Stdout, []string{}, rows)
+			err = printers.TablePrinter([]string{}).Print(os.Stdout, rows)
+			if err != nil {
+				return false, err
+			}
 		}
 
 		return false, nil
diff --git a/internal/build/diff.go b/internal/build/diff.go
index 4c6b6efa503303567672e4a65cc8972f3a7882e5..b955989c925713454856f3e64ef3cfb3683c1c8e 100644
--- a/internal/build/diff.go
+++ b/internal/build/diff.go
@@ -27,6 +27,7 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/fluxcd/flux2/pkg/printers"
 	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1beta2"
 	"github.com/fluxcd/pkg/ssa"
 	"github.com/gonvenience/bunt"
@@ -112,7 +113,7 @@ func (b *Builder) Diff() (string, bool, error) {
 		}
 
 		if change.Action == string(ssa.ConfiguredAction) {
-			output.WriteString(writeString(fmt.Sprintf("â–º %s drifted\n", change.Subject), bunt.WhiteSmoke))
+			output.WriteString(bunt.Sprint(fmt.Sprintf("â–º %s drifted\n", change.Subject)))
 			liveFile, mergedFile, tmpDir, err := writeYamls(liveObject, mergedObject)
 			if err != nil {
 				return "", createdOrDrifted, err
@@ -204,14 +205,9 @@ func diff(liveFile, mergedFile string, output io.Writer) error {
 		return fmt.Errorf("failed to compare input files: %w", err)
 	}
 
-	reportWriter := &dyff.HumanReport{
-		Report:     report,
-		OmitHeader: true,
-	}
+	printer := printers.NewDyffPrinter()
 
-	if err := reportWriter.WriteReport(output); err != nil {
-		return fmt.Errorf("failed to print report: %w", err)
-	}
+	printer.Print(output, report)
 
 	return nil
 }
diff --git a/internal/utils/utils.go b/internal/utils/utils.go
index 361b944852f044c67454421e82641a4697d707e6..cdadeaa406fb2edf8b0eb933b21852848930b31d 100644
--- a/internal/utils/utils.go
+++ b/internal/utils/utils.go
@@ -27,7 +27,6 @@ import (
 	"runtime"
 	"strings"
 
-	"github.com/olekukonko/tablewriter"
 	appsv1 "k8s.io/api/apps/v1"
 	corev1 "k8s.io/api/core/v1"
 	networkingv1 "k8s.io/api/networking/v1"
@@ -247,24 +246,6 @@ func MakeDependsOn(deps []string) []dependency.CrossNamespaceDependencyReference
 	return refs
 }
 
-func PrintTable(writer io.Writer, header []string, rows [][]string) {
-	table := tablewriter.NewWriter(writer)
-	table.SetHeader(header)
-	table.SetAutoWrapText(false)
-	table.SetAutoFormatHeaders(true)
-	table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
-	table.SetAlignment(tablewriter.ALIGN_LEFT)
-	table.SetCenterSeparator("")
-	table.SetColumnSeparator("")
-	table.SetRowSeparator("")
-	table.SetHeaderLine(false)
-	table.SetBorder(false)
-	table.SetTablePadding("\t")
-	table.SetNoWhiteSpace(true)
-	table.AppendBulk(rows)
-	table.Render()
-}
-
 func ValidateComponents(components []string) error {
 	defaults := install.MakeDefaultOptions()
 	bootstrapAllComponents := append(defaults.Components, defaults.ComponentsExtra...)
diff --git a/pkg/printers/dyff.go b/pkg/printers/dyff.go
new file mode 100644
index 0000000000000000000000000000000000000000..50db14d2f8f716758977df6bc1a9b8fdf6954c7a
--- /dev/null
+++ b/pkg/printers/dyff.go
@@ -0,0 +1,56 @@
+/*
+Copyright 2022 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package printers
+
+import (
+	"fmt"
+	"io"
+
+	"github.com/homeport/dyff/pkg/dyff"
+)
+
+// DyffPrinter is a printer that prints dyff reports.
+type DyffPrinter struct {
+	OmitHeader bool
+}
+
+// NewDyffPrinter returns a new DyffPrinter.
+func NewDyffPrinter() *DyffPrinter {
+	return &DyffPrinter{
+		OmitHeader: true,
+	}
+}
+
+// Print prints the given args to the given writer.
+func (p *DyffPrinter) Print(w io.Writer, args ...interface{}) error {
+	for _, arg := range args {
+		switch arg := arg.(type) {
+		case dyff.Report:
+			reportWriter := &dyff.HumanReport{
+				Report:     arg,
+				OmitHeader: p.OmitHeader,
+			}
+
+			if err := reportWriter.WriteReport(w); err != nil {
+				return fmt.Errorf("failed to print report: %w", err)
+			}
+		default:
+			return fmt.Errorf("unsupported type %T", arg)
+		}
+	}
+	return nil
+}
diff --git a/pkg/printers/interface.go b/pkg/printers/interface.go
new file mode 100644
index 0000000000000000000000000000000000000000..e01600e5b03657fba1876a43a3301eb147b985a0
--- /dev/null
+++ b/pkg/printers/interface.go
@@ -0,0 +1,33 @@
+/*
+Copyright 2022 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package printers
+
+import "io"
+
+// Printer is an interface for printing Flux cmd	outputs.
+type Printer interface {
+	// Print prints the given args to the given writer.
+	Print(io.Writer, ...interface{}) error
+}
+
+// PrinterFunc is a function that can print args to a writer.
+type PrinterFunc func(w io.Writer, args ...interface{}) error
+
+// Print implements Printer
+func (fn PrinterFunc) Print(w io.Writer, args ...interface{}) error {
+	return fn(w, args)
+}
diff --git a/pkg/printers/table_printer.go b/pkg/printers/table_printer.go
new file mode 100644
index 0000000000000000000000000000000000000000..716166ab10e38cebfe60f33aa9787e911091b7e3
--- /dev/null
+++ b/pkg/printers/table_printer.go
@@ -0,0 +1,65 @@
+/*
+Copyright 2022 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package printers
+
+import (
+	"fmt"
+	"io"
+
+	"github.com/olekukonko/tablewriter"
+)
+
+// TablePrinter is a printer that prints Flux cmd	outputs.
+func TablePrinter(header []string) PrinterFunc {
+	return func(w io.Writer, args ...interface{}) error {
+		var rows [][]string
+		for _, arg := range args {
+			switch arg := arg.(type) {
+			case []interface{}:
+				for _, v := range arg {
+					s, ok := v.([][]string)
+					if !ok {
+						return fmt.Errorf("unsupported type %T", v)
+					}
+					for i := range s {
+						rows = append(rows, s[i])
+					}
+				}
+			default:
+				return fmt.Errorf("unsupported type %T", arg)
+			}
+		}
+
+		table := tablewriter.NewWriter(w)
+		table.SetHeader(header)
+		table.SetAutoWrapText(false)
+		table.SetAutoFormatHeaders(true)
+		table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
+		table.SetAlignment(tablewriter.ALIGN_LEFT)
+		table.SetCenterSeparator("")
+		table.SetColumnSeparator("")
+		table.SetRowSeparator("")
+		table.SetHeaderLine(false)
+		table.SetBorder(false)
+		table.SetTablePadding("\t")
+		table.SetNoWhiteSpace(true)
+		table.AppendBulk(rows)
+		table.Render()
+
+		return nil
+	}
+}