diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1e5546a04b..13f8c6bcc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## [Unreleased]
+
+### Added
+
+- New `fsharp_keep_single_case_union_multiline` setting to keep single-case discriminated union declarations on a separate line instead of collapsing them onto the type name. [#3377](https://github.com/fsprojects/fantomas/pull/3377)
+
## [8.0.0-alpha-012] - 2026-04-16
### Changed
diff --git a/docs/docs/end-users/Configuration.fsx b/docs/docs/end-users/Configuration.fsx
index 98e358cbfd..3e0a0b59f0 100644
--- a/docs/docs/end-users/Configuration.fsx
+++ b/docs/docs/end-users/Configuration.fsx
@@ -1020,7 +1020,7 @@ printfn
(*** include-output ***)
formatCode
- """
+ """
type MyDU = Short of int
"""
"""
@@ -1029,6 +1029,29 @@ fsharp_bar_before_discriminated_union_declaration = true
(*** include-output ***)
+(**
+
+### fsharp_keep_single_case_union_multiline
+
+
+Keep the declaration of a single-case discriminated union on a separate line, instead of collapsing it onto the same line as the type name when it is short enough to fit.
+*)
+
+(*** hide ***)
+printfn
+ $"# Default\n{toEditorConfigName (nameof FormatConfig.Default.KeepSingleCaseUnionMultiline)} = {FormatConfig.Default.KeepSingleCaseUnionMultiline.ToString().ToLower()}"
+(*** include-output ***)
+
+formatCode
+ """
+ type MyDU = Short of int
+ """
+ """
+fsharp_keep_single_case_union_multiline = true
+ """
+
+(*** include-output ***)
+
(**
## Other
diff --git a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj
index f1903212d9..8232f1eba7 100644
--- a/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj
+++ b/src/Fantomas.Core.Tests/Fantomas.Core.Tests.fsproj
@@ -82,6 +82,7 @@
+
diff --git a/src/Fantomas.Core.Tests/KeepSingleCaseUnionMultilineTests.fs b/src/Fantomas.Core.Tests/KeepSingleCaseUnionMultilineTests.fs
new file mode 100644
index 0000000000..41da10cf75
--- /dev/null
+++ b/src/Fantomas.Core.Tests/KeepSingleCaseUnionMultilineTests.fs
@@ -0,0 +1,141 @@
+module Fantomas.Core.Tests.KeepSingleCaseUnionMultilineTests
+
+open NUnit.Framework
+open FsUnit
+open Fantomas.Core.Tests.TestHelpers
+
+let config =
+ { config with
+ KeepSingleCaseUnionMultiline = true }
+
+[]
+let ``single case union with field is kept multiline`` () =
+ formatSourceString
+ """
+type MyDU = Short of int
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type MyDU =
+ | Short of int
+"""
+
+[]
+let ``single case union without field is kept multiline`` () =
+ formatSourceString
+ """
+type A = | A
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type A =
+ | A
+"""
+
+[]
+let ``single case union with access modifier is kept multiline`` () =
+ formatSourceString
+ """
+type Foo = private Foo of int
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type Foo =
+ private | Foo of int
+"""
+
+[]
+let ``result is idempotent`` () =
+ let source =
+ """
+type MyDU =
+ | Short of int
+"""
+
+ formatSourceString source config
+ |> fun formatted -> formatSourceString formatted config
+ |> prepend newline
+ |> should
+ equal
+ """
+type MyDU =
+ | Short of int
+"""
+
+[]
+let ``combines with bar before discriminated union declaration`` () =
+ formatSourceString
+ """
+type MyDU = Short of int
+"""
+ { config with
+ BarBeforeDiscriminatedUnionDeclaration = true }
+ |> prepend newline
+ |> should
+ equal
+ """
+type MyDU =
+ | Short of int
+"""
+
+[]
+let ``multi case union is unaffected`` () =
+ formatSourceString
+ """
+type MyDU =
+ | Short of int
+ | Long of string
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type MyDU =
+ | Short of int
+ | Long of string
+"""
+
+[]
+let ``single case union with member stays multiline`` () =
+ formatSourceString
+ """
+type MyDU =
+ | Short of int
+ member this.Value = 1
+"""
+ config
+ |> prepend newline
+ |> should
+ equal
+ """
+type MyDU =
+ | Short of int
+
+ member this.Value = 1
+"""
+
+[]
+let ``single case union in signature file is kept multiline`` () =
+ formatSignatureString
+ """namespace meh
+
+type Foo = Bar of int
+"""
+ config
+ |> should
+ equal
+ """namespace meh
+
+type Foo =
+ | Bar of int
+"""
diff --git a/src/Fantomas.Core/CodePrinter.fs b/src/Fantomas.Core/CodePrinter.fs
index d793de426b..71212a547c 100644
--- a/src/Fantomas.Core/CodePrinter.fs
+++ b/src/Fantomas.Core/CodePrinter.fs
@@ -3549,10 +3549,13 @@ let genTypeDefn (td: TypeDefn) =
genSingleTextNode vis +> onlyIfNot singleCase.XmlDoc.IsNone sepNln)
+> genUnionCase hasVerticalBar singleCase
- expressionFitsOnRestOfLine
- (sepSpace +> genCase hasVerticalBar)
- (indentSepNlnUnindent (genCase true))
- ctx
+ if ctx.Config.KeepSingleCaseUnionMultiline then
+ indentSepNlnUnindent (genCase true) ctx
+ else
+ expressionFitsOnRestOfLine
+ (sepSpace +> genCase hasVerticalBar)
+ (indentSepNlnUnindent (genCase true))
+ ctx
| xs ->
indentSepNlnUnindent
(opt sepNln node.Accessibility genSingleTextNode
diff --git a/src/Fantomas.Core/FormatConfig.fs b/src/Fantomas.Core/FormatConfig.fs
index b96dda2856..54069789c6 100644
--- a/src/Fantomas.Core/FormatConfig.fs
+++ b/src/Fantomas.Core/FormatConfig.fs
@@ -221,6 +221,11 @@ type FormatConfig =
[]
BarBeforeDiscriminatedUnionDeclaration: bool
+ []
+ []
+ []
+ KeepSingleCaseUnionMultiline: bool
+
[]
[]
[]
@@ -273,6 +278,7 @@ type FormatConfig =
ExperimentalKeepIndentInBranch = false
BlankLinesAroundNestedMultilineExpressions = true
BarBeforeDiscriminatedUnionDeclaration = false
+ KeepSingleCaseUnionMultiline = false
MultilineBracketStyle = Aligned
KeepMaxNumberOfBlankLines = 100
NewlineBeforeMultilineComputationExpression = true