diff --git a/src/Microsoft.DotNet.Interactive.Formatting.Tests/BinaryFormatterTests.cs b/src/Microsoft.DotNet.Interactive.Formatting.Tests/BinaryFormatterTests.cs
new file mode 100644
index 0000000000..bf205b8003
--- /dev/null
+++ b/src/Microsoft.DotNet.Interactive.Formatting.Tests/BinaryFormatterTests.cs
@@ -0,0 +1,110 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+using FluentAssertions;
+using Microsoft.DotNet.Interactive.Formatting.Tests.Utility;
+using Xunit;
+
+namespace Microsoft.DotNet.Interactive.Formatting.Tests;
+
+public class BinaryFormatterTests : FormatterTestBase
+{
+ [Fact]
+ public void Byte_array_formats_as_hex_dump_in_plain_text()
+ {
+ var bytes = new byte[] {
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21, // "Hello World!"
+ 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21
+ };
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().Contain("00000000");
+ formatted.Should().Contain("48 65 6C 6C 6F 20 57 6F");
+ formatted.Should().Contain("|Hello World!");
+ }
+
+ [Fact]
+ public void Byte_array_formats_as_hex_dump_in_html()
+ {
+ var bytes = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
+
+ var formatted = bytes.ToDisplayString(HtmlFormatter.MimeType).RemoveStyleElement();
+
+ formatted.Should().Contain("
");
+ formatted.Should().Contain("00000000");
+ formatted.Should().Contain("48 65 6C 6C 6F");
+ formatted.Should().Contain("");
+ }
+
+ [Fact]
+ public void Empty_byte_array_produces_empty_output()
+ {
+ var bytes = Array.Empty();
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().BeEmpty();
+ }
+
+ [Fact]
+ public void Null_byte_array_shows_null_string()
+ {
+ byte[] bytes = null;
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().Contain(Formatter.NullString);
+ }
+
+ [Fact]
+ public void Long_byte_array_spans_multiple_lines()
+ {
+ var bytes = new byte[32]; // Two lines worth
+ for (int i = 0; i < bytes.Length; i++)
+ {
+ bytes[i] = (byte)i;
+ }
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().Contain("00000000");
+ formatted.Should().Contain("00000010");
+ }
+
+ [Fact]
+ public void Non_printable_characters_show_as_dots()
+ {
+ var bytes = new byte[] { 0x00, 0x01, 0x02, 0xFF };
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().Contain("|....|");
+ }
+
+ [Fact]
+ public void ReadOnlyMemory_byte_formats_like_byte_array()
+ {
+ var bytes = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F };
+ var memory = new ReadOnlyMemory(bytes);
+
+ var formatted = memory.ToDisplayString(PlainTextFormatter.MimeType);
+
+ formatted.Should().Contain("00000000");
+ formatted.Should().Contain("48 65 6C 6C 6F");
+ }
+
+ [Fact]
+ public void Partial_last_line_is_padded_correctly()
+ {
+ var bytes = new byte[] { 0x01, 0x02, 0x03, 0x04 }; // Only 4 bytes
+
+ var formatted = bytes.ToDisplayString(PlainTextFormatter.MimeType);
+
+ // Should have proper spacing even with fewer than 16 bytes
+ formatted.Should().Contain("01 02 03 04");
+ formatted.Should().Contain("|....|");
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.DotNet.Interactive.Formatting/BinaryFormatter.cs b/src/Microsoft.DotNet.Interactive.Formatting/BinaryFormatter.cs
new file mode 100644
index 0000000000..9e5e9734e8
--- /dev/null
+++ b/src/Microsoft.DotNet.Interactive.Formatting/BinaryFormatter.cs
@@ -0,0 +1,135 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+
+namespace Microsoft.DotNet.Interactive.Formatting;
+
+///
+/// Provides formatting for binary data (byte arrays) with hexadecimal representation.
+///
+public static class BinaryFormatter
+{
+ private const int BytesPerLine = 16;
+
+ public static string FormatBytes(byte[] bytes)
+ {
+ if (bytes is null || bytes.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ using var writer = new StringWriter();
+ FormatBytesTo(bytes, writer);
+ return writer.ToString();
+ }
+
+ public static void FormatBytesTo(byte[] bytes, TextWriter writer)
+ {
+ if (bytes is null || bytes.Length == 0)
+ {
+ return;
+ }
+
+ for (int offset = 0; offset < bytes.Length; offset += BytesPerLine)
+ {
+ // Write address offset
+ writer.Write($"{offset:X8} ");
+
+ // Write hex values
+ int bytesInLine = Math.Min(BytesPerLine, bytes.Length - offset);
+
+ for (int i = 0; i < BytesPerLine; i++)
+ {
+ if (i < bytesInLine)
+ {
+ writer.Write($"{bytes[offset + i]:X2} ");
+ }
+ else
+ {
+ writer.Write(" ");
+ }
+
+ // Add extra space after 8 bytes for readability
+ if (i == 7)
+ {
+ writer.Write(" ");
+ }
+ }
+
+ // Write ASCII representation
+ writer.Write(" |");
+ for (int i = 0; i < bytesInLine; i++)
+ {
+ byte b = bytes[offset + i];
+ char c = (b >= 32 && b < 127) ? (char)b : '.';
+ writer.Write(c);
+ }
+ writer.Write("|");
+
+ if (offset + BytesPerLine < bytes.Length)
+ {
+ writer.WriteLine();
+ }
+ }
+ }
+
+ internal static ITypeFormatter[] DefaultFormatters { get; } =
+ {
+ // PlainText formatter for byte arrays
+ new PlainTextFormatter((bytes, context) =>
+ {
+ if (bytes is null)
+ {
+ context.Writer.Write(Formatter.NullString);
+ return true;
+ }
+
+ FormatBytesTo(bytes, context.Writer);
+ return true;
+ }),
+
+ // HTML formatter for byte arrays
+ new HtmlFormatter((bytes, context) =>
+ {
+ if (bytes is null)
+ {
+ context.Writer.Write(Formatter.NullString);
+ return true;
+ }
+
+ context.Writer.Write("");
+ FormatBytesTo(bytes, context.Writer);
+ context.Writer.Write("");
+ return true;
+ }),
+
+ // PlainText formatter for ReadOnlyMemory
+ new AnonymousTypeFormatter