Starting with .NET 10, dotnet new sln generates .slnx files by default. Your old .sln files still work, but the writing is on the wall — the .sln format that has haunted .NET developers for over two decades is finally being replaced.
If you’ve ever opened a .sln file and seen a wall of GUIDs, cryptic section headers, and formatting that makes zero sense to humans — you already know why this change was needed. The new .slnx format is XML-based, human-readable, and dramatically easier to work with in Git.
In this article, we’ll cover what .slnx is, why it exists, how to migrate your existing solutions, and everything you need to know about tooling, CI/CD compatibility, and rolling this out across your team.
Let’s get into it.
What is the .slnx File Format?
The .slnx format is a new XML-based solution file format introduced by Microsoft to replace the legacy .sln format. It describes the same thing — which projects belong to a solution, how they’re organized into folders, and build configurations — but in clean, readable XML instead of the proprietary text format that .sln used.
In .NET 10, .slnx is the default format when you create a new solution with the dotnet new sln command. This is an official breaking change documented by Microsoft.
The recommended solution file format for .NET 10 and beyond is
.slnx. It’s supported by Visual Studio 2022 (17.13+), JetBrains Rider, VS Code with C# Dev Kit, MSBuild 17.12+, and the .NET CLI (SDK 9.0.200+).
Here’s why developers are excited about it:
- Human-readable XML — you can open it in any text editor and understand it instantly
- No more GUIDs — project type GUIDs and solution GUIDs are gone
- Fewer merge conflicts — the simplified structure means Git diffs are tiny and predictable
- Consistent with
.csproj— the solution file finally speaks the same language as your project files
The Problem with .sln Files
Haven’t we all stared at a .sln file in confusion at some point? Here’s what a typical .sln file looks like for a simple WebAPI project with two class libraries:
Microsoft Visual Studio Solution File, Format Version 12.00# Visual Studio Version 17VisualStudioVersion = 17.0.31903.59MinimumVisualStudioVersion = 10.0.40219.1Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Api", "MyApp.Api\MyApp.Api.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"EndProjectProject("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Core", "MyApp.Core\MyApp.Core.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"EndProjectProject("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApp.Infrastructure", "MyApp.Infrastructure\MyApp.Infrastructure.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"EndProjectProject("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4E5F6A7-B8C9-0123-DEFA-234567890123}"EndProjectGlobal GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {D4E5F6A7-B8C9-0123-DEFA-234567890123} {B2C3D4E5-F6A7-8901-BCDE-F12345678901} = {D4E5F6A7-B8C9-0123-DEFA-234567890123} {C3D4E5F6-A7B8-9012-CDEF-123456789012} = {D4E5F6A7-B8C9-0123-DEFA-234567890123} EndGlobalSectionEndGlobalThat’s 35 lines for three projects. And the real pain points?
- GUIDs everywhere —
{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}is a “C# project type GUID.” Good luck remembering that. - Merge conflict nightmare — Two developers add a project on different branches? The GUID-based
NestedProjectsandProjectConfigurationPlatformssections create conflicts that are nearly impossible to resolve by hand. - Non-standard format — It’s not JSON, not XML, not YAML. It’s a custom format that only Visual Studio truly understands. You can’t validate it with any standard tool.
- Redundant configuration — Every project gets Debug and Release configuration entries duplicated, even when they all use the same settings.
This format was designed in the early 2000s when Visual Studio was the only way to work with .NET. It was never meant to be edited by humans or merged in Git. The .csproj format was modernized back in .NET Core — it was about time the solution file caught up.
.sln vs .slnx — Side-by-Side Comparison
Here’s that same three-project solution in .slnx format:
<Solution> <Folder Name="src"> <Project Path="MyApp.Api/MyApp.Api.csproj" /> <Project Path="MyApp.Core/MyApp.Core.csproj" /> <Project Path="MyApp.Infrastructure/MyApp.Infrastructure.csproj" /> </Folder></Solution>That’s 7 lines. Down from 35. Let that sink in.
No GUIDs. No configuration platform mappings. No cryptic section headers. Just a clean XML tree that says: “Here are my projects, and here’s how they’re organized.”
| Feature | .sln (Legacy) | .slnx (New) |
|---|---|---|
| Format | Proprietary text | Standard XML |
| Human-readable | Barely | Completely |
| GUIDs | Required for every project and folder | None |
| Lines for 3 projects | ~35 | ~7 |
| Git merge conflicts | Frequent and painful | Rare and simple |
| Configuration platforms | Explicitly listed per project | Inferred from project files |
| Tooling | VS, MSBuild, .NET CLI | VS 2022 17.13+, Rider, VS Code, MSBuild 17.12+, .NET CLI 9.0.200+ |
| Default in .NET 10 | No | Yes |
| Editable in Notepad | Technically yes, practically no | Absolutely |
The .slnx format infers build configurations from the project files themselves, which is why you don’t see Debug|Any CPU mappings. If all your projects use the standard Debug/Release configurations (which 99% of projects do), the .slnx file just works without listing them.
The .NET 10 Breaking Change
In .NET 10, this is now a breaking change. When you run:
dotnet new slnYou get a .slnx file instead of a .sln file. The generated content is minimal:
<Solution></Solution>If you specifically need the old .sln format (for legacy tooling compatibility, for example), you can opt out:
dotnet new sln --format slnThis change was introduced because the .NET SDK added .slnx support in version 9.0.200, and it’s been stable and well-supported across all major .NET tooling since then.
PRO TIP: If you’re starting a new .NET 10 project today, you’ll get
.slnxautomatically. There’s no reason to use.slnfor new projects.
How to Migrate from .sln to .slnx
There are three ways to migrate, depending on your preferred workflow.
Method 1: Using the .NET CLI (Recommended)
This is the fastest and most reliable method. Run this command in the directory containing your .sln file:
dotnet sln MyApp.sln migrateThis generates a new MyApp.slnx file based on your existing .sln. The original .sln file is not deleted — it stays intact so you can validate the migration before removing it.
If you only have one .sln file in the directory, you can simplify:
dotnet sln migrateThe CLI automatically finds the .sln file and creates the .slnx equivalent.
Important: You need .NET SDK 9.0.200 or later for the
migratecommand. Check your version withdotnet --version.
Method 2: Using Visual Studio
If you prefer the GUI approach:
- Open your solution in Visual Studio 2022 (version 17.13 or later)
- Go to File > Save Solution As…
- In the file type dropdown, change it to Xml Solution File (*.slnx)
- Click Save
This creates the .slnx file alongside your existing .sln. You’ll want to remove the old .sln file after validating.
Method 3: Using JetBrains Rider
Rider has supported .slnx since version 2024.3. To migrate:
- Open your solution in Rider
- Right-click the solution node in the Solution Explorer
- Select Save As .slnx
Same deal — the old .sln file is preserved for you to validate.
Migrating a Real Multi-Project Solution
Let’s walk through migrating a real-world WebAPI solution instead of just showing the command. Say you have a typical layered architecture:
MyApp/├── MyApp.sln├── src/│ ├── MyApp.Api/│ │ └── MyApp.Api.csproj│ ├── MyApp.Core/│ │ └── MyApp.Core.csproj│ └── MyApp.Infrastructure/│ └── MyApp.Infrastructure.csproj└── tests/ ├── MyApp.UnitTests/ │ └── MyApp.UnitTests.csproj └── MyApp.IntegrationTests/ └── MyApp.IntegrationTests.csprojStep 1: Verify Your SDK Version
dotnet --versionMake sure you’re on 9.0.200 or later (ideally .NET 10 SDK).
Step 2: Run the Migration
dotnet sln MyApp.sln migrateYou’ll see output confirming the migration:
The solution file 'MyApp.sln' has been migrated to 'MyApp.slnx'.Step 3: Inspect the Generated .slnx
Open MyApp.slnx. You should see something like:
<Solution> <Folder Name="src"> <Project Path="src/MyApp.Api/MyApp.Api.csproj" /> <Project Path="src/MyApp.Core/MyApp.Core.csproj" /> <Project Path="src/MyApp.Infrastructure/MyApp.Infrastructure.csproj" /> </Folder> <Folder Name="tests"> <Project Path="tests/MyApp.UnitTests/MyApp.UnitTests.csproj" /> <Project Path="tests/MyApp.IntegrationTests/MyApp.IntegrationTests.csproj" /> </Folder></Solution>Notice how solution folders are preserved as <Folder> elements, and nested projects appear as children of their folder. Clean and intuitive.
Step 4: Validate the Build
dotnet build MyApp.slnxRun your tests too:
dotnet test MyApp.slnxIf both pass, you’re good to proceed.
Step 5: Remove the Old .sln File
Once you’ve confirmed everything works:
rm MyApp.slnDo NOT keep both files in the same repository. Having both
.slnand.slnxcauses confusion — thedotnet slncommand won’t know which one to use and will prompt every time. Pick one and commit to it.
Solution Folders and Nested Projects in .slnx
One concern developers have is whether .slnx handles solution folders properly. It does — and it’s much cleaner than the old format.
In .sln, solution folders required a special project type GUID ({2150E333-8FDC-42A3-9474-1A3956D46DE8}) and a NestedProjects section to map projects into folders using GUIDs. It looked like this:
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{SOME-GUID}"EndProject...GlobalSection(NestedProjects) = preSolution {PROJECT-GUID} = {FOLDER-GUID}EndGlobalSectionIn .slnx, it’s just XML nesting:
<Solution> <Folder Name="src"> <Project Path="src/MyApp.Api/MyApp.Api.csproj" /> </Folder> <Folder Name="tests"> <Project Path="tests/MyApp.Tests/MyApp.Tests.csproj" /> </Folder></Solution>You can even nest folders inside folders:
<Solution> <Folder Name="src"> <Folder Name="services"> <Project Path="src/services/OrderService/OrderService.csproj" /> <Project Path="src/services/PaymentService/PaymentService.csproj" /> </Folder> <Folder Name="shared"> <Project Path="src/shared/Common/Common.csproj" /> </Folder> </Folder></Solution>Get the point? The XML structure mirrors the logical organization of your solution. No more cross-referencing GUIDs to figure out which project belongs to which folder.
Tooling Support Matrix
Here’s the current state of .slnx support across the .NET ecosystem:
| Tool | Version Required | Status |
|---|---|---|
| Visual Studio 2022 | 17.13+ | Full support (open, edit, Save As) |
| JetBrains Rider | 2024.3+ | Full support |
| VS Code + C# Dev Kit | Latest | Full support |
| .NET CLI | SDK 9.0.200+ | Full support (create, migrate, add, remove, list) |
| MSBuild | 17.12+ | Full support |
GitHub Actions (dotnet build) | Uses SDK version | Works if SDK ≥ 9.0.200 |
Azure DevOps (dotnet build) | Uses SDK version | Works if SDK ≥ 9.0.200 |
Docker (dotnet build in Dockerfile) | Uses base image SDK | Works with mcr.microsoft.com/dotnet/sdk:10.0 |
The key takeaway: if your tooling is reasonably up to date (2024+), .slnx just works. The only scenario where you might hit issues is with very old third-party extensions or custom MSBuild tasks that parse .sln files directly.
CI/CD Pipeline Compatibility
This is the question nobody else answers: does my CI/CD pipeline break when I switch to .slnx?
The short answer: no. If your pipeline uses standard dotnet commands, it works identically.
GitHub Actions
Here’s a typical GitHub Actions workflow. Nothing changes — dotnet build and dotnet test detect the .slnx file automatically:
name: Build and Test
on: push: branches: [main] pull_request: branches: [main]
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup .NET uses: actions/setup-dotnet@v4 with: dotnet-version: '10.0.x'
- name: Restore run: dotnet restore
- name: Build run: dotnet build --no-restore --configuration Release
- name: Test run: dotnet test --no-build --configuration ReleaseThe commands are the same whether you have a .sln or .slnx file. The .NET CLI auto-discovers the solution file in the current directory.
One thing to watch: If your pipeline explicitly references the
.slnfile by name (e.g.,dotnet build MyApp.sln), you’ll need to update that reference toMyApp.slnx.
Docker Builds
Same story with Docker. If your Dockerfile copies and builds the solution:
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS buildWORKDIR /srcCOPY ["MyApp.slnx", "."]COPY ["src/MyApp.Api/MyApp.Api.csproj", "src/MyApp.Api/"]COPY ["src/MyApp.Core/MyApp.Core.csproj", "src/MyApp.Core/"]RUN dotnet restoreCOPY . .RUN dotnet publish "src/MyApp.Api/MyApp.Api.csproj" -c Release -o /app/publishJust make sure you’re copying the .slnx file instead of .sln. That’s the only change.
Git Merge Conflicts: Before vs After
Everyone says .slnx reduces merge conflicts, but let’s actually prove it. Here’s a real scenario:
Scenario: Two developers on different branches each add a new project to the solution.
With .sln — Merge Conflict
Developer A adds NotificationService:
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NotificationService", "src\NotificationService\NotificationService.csproj", "{NEW-GUID-A}"EndProject... {NEW-GUID-A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {NEW-GUID-A}.Debug|Any CPU.Build.0 = Debug|Any CPU {NEW-GUID-A}.Release|Any CPU.ActiveCfg = Release|Any CPU {NEW-GUID-A}.Release|Any CPU.Build.0 = Release|Any CPU... {NEW-GUID-A} = {FOLDER-GUID}Developer B adds EmailService and gets the same kind of changes. When they merge, Git sees conflicting changes in three different sections of the .sln file — the project declarations, the configuration platforms, and the nested projects section. Resolving this by hand is error-prone and tedious.
With .slnx — Clean Merge
Developer A’s diff:
<Folder Name="src"> <Project Path="src/NotificationService/NotificationService.csproj" /></Folder>Developer B’s diff:
<Folder Name="src"> <Project Path="src/EmailService/EmailService.csproj" /></Folder>Git can auto-merge these changes because they’re on different lines within the same XML block. No conflict. No manual resolution. Trust me, once you experience this on a team with 5+ developers all working on the same solution, you’ll never go back.
Team Adoption Checklist
Migrating your own solution is easy. Rolling it out across a team takes a bit more planning. Here’s a checklist:
1. Update .gitignore
Add the old .sln file to .gitignore so nobody accidentally adds it back:
# Legacy solution files (migrated to .slnx)*.slnOr if you want to be more targeted, ignore only the specific file:
MyApp.sln2. Verify Everyone’s SDK Version
Run this as a team:
dotnet --versionEveryone needs SDK 9.0.200+ (ideally .NET 10 SDK). If anyone is behind, they won’t be able to open the .slnx file.
3. Create a Single Migration PR
Don’t sneak the migration into a feature PR. Create a dedicated migration PR that:
- Adds the new
.slnxfile - Removes the old
.slnfile - Updates any CI/CD configs that reference the
.slnby name - Updates the
.gitignore - Updates the
README.mdif it references the.slnfile
This makes the change reviewable and easy to revert if something goes wrong.
4. Update IDE-Specific Launch Configs
If your team uses .vscode/launch.json or launchSettings.json files that reference the solution by name, update those too.
5. Communicate the Change
Post in your team’s Slack/Teams channel:
“We’ve migrated from .sln to .slnx. Make sure your SDK is 9.0.200+ and pull the latest. If your IDE doesn’t recognize the file, update to the latest version.”
Simple. No drama.
Troubleshooting Common Issues
”dotnet sln migrate” Command Not Found
You’re on an older SDK version. Check with dotnet --version. You need 9.0.200 or later. Update your SDK:
dotnet --versionIf you’re below 9.0.200, download the latest SDK from dotnet.microsoft.com.
”Multiple solution files found” Error
If you have both .sln and .slnx in the same directory, the CLI doesn’t know which one to use. This is expected — remove the old .sln file after validating the migration. If you need to specify which file to use:
dotnet build MyApp.slnxSolution Folders Are Missing After Migration
This is rare, but if your .sln file had unusual folder nesting, double-check the generated .slnx. The migration tool handles standard layouts well, but exotic folder structures might need a manual tweak. Open the .slnx in any text editor and verify the <Folder> elements look correct.
Third-Party Tools Don’t Recognize .slnx
Some older tools that parse .sln files directly (custom code generators, legacy build scripts) may not support .slnx yet. Options:
- Check if the tool has an updated version with
.slnxsupport - Keep a
.slnfile generated from.slnxfor that specific tool (not ideal, but a temporary workaround) - Use the Microsoft.VisualStudio.SolutionPersistence NuGet package if you’re building tools that need to read solution files — it supports both formats
Build Fails in CI After Migration
If your CI pipeline explicitly references MyApp.sln, update it to MyApp.slnx. Also verify the CI environment has .NET SDK 9.0.200+ (or .NET 10 SDK). Most hosted runners (GitHub Actions ubuntu-latest, Azure DevOps Microsoft-hosted agents) are updated regularly, but self-hosted runners may need a manual SDK update.
Key Takeaways
.slnxis the default in .NET 10. New solutions created withdotnet new slnnow generate.slnxfiles automatically.- Migration takes one command:
dotnet sln migrateconverts your.slnto.slnxinstantly. - Git merge conflicts drop dramatically because the XML format is predictable, compact, and doesn’t rely on GUIDs.
- All major tooling supports it: Visual Studio 2022 (17.13+), Rider (2024.3+), VS Code, MSBuild (17.12+), and the .NET CLI (SDK 9.0.200+).
- CI/CD pipelines work unchanged — just update any hardcoded
.slnfile references.
Frequently Asked Questions
What is the .slnx file format in .NET?
The .slnx format is a new XML-based solution file format introduced by Microsoft to replace the legacy .sln format. It describes which projects belong to a solution, how they are organized into folders, and build configurations — but in clean, readable XML instead of the proprietary text format that .sln files used. It is the default format in .NET 10.
How do I migrate from .sln to .slnx?
The fastest way is using the .NET CLI. Run dotnet sln YourSolution.sln migrate in your terminal. This creates a new .slnx file while keeping the original .sln file intact. You can also use Visual Studio 2022 (File > Save Solution As > Xml Solution File) or JetBrains Rider (right-click solution > Save As .slnx). You need .NET SDK 9.0.200 or later.
Is .slnx the default format in .NET 10?
Yes. Starting with .NET 10, the dotnet new sln command generates a .slnx file instead of a .sln file. This is an official breaking change. If you need the old .sln format, you can opt out by running dotnet new sln --format sln.
Does .slnx work with Visual Studio, Rider, and VS Code?
Yes. Visual Studio 2022 version 17.13 and later, JetBrains Rider 2024.3 and later, and VS Code with the C# Dev Kit all fully support .slnx files. You can open, edit, build, and debug solutions using the .slnx format in all three IDEs.
Can I have both .sln and .slnx files in the same repository?
You can, but it is strongly recommended that you do not. Having both files causes confusion because the dotnet sln command will not know which solution file to use and will prompt you every time. It also creates a maintenance burden of keeping both files in sync. Migrate to .slnx and remove the old .sln file.
Does .slnx support solution folders?
Yes. Solution folders in .slnx are represented as Folder XML elements with nested Project elements inside them. This is far cleaner than the old .sln approach which required special project type GUIDs and a separate NestedProjects section. You can also nest folders inside folders using standard XML nesting.
Will .slnx work with my CI/CD pipeline?
Yes. If your CI/CD pipeline uses standard dotnet commands like dotnet build, dotnet test, and dotnet publish, it works identically with .slnx files. The only change needed is updating any hardcoded references from YourSolution.sln to YourSolution.slnx. Make sure your build environment has .NET SDK 9.0.200 or later.
Should I migrate existing projects to .slnx right now?
Yes, if your team is on .NET SDK 9.0.200 or later and your tooling supports it. The migration is low-risk because the original .sln file is preserved and you can validate before removing it. The benefits of fewer merge conflicts, better readability, and alignment with the .NET 10 default make it worth migrating sooner rather than later.
Summary
The .slnx format is one of those changes that seems small but makes a real difference in your daily workflow. No more GUIDs. No more unreadable solution files. No more dreading merge conflicts when two people add projects to the same solution.
With .NET 10 making .slnx the default, now is the time to migrate. It’s a one-command operation (dotnet sln migrate), all major tooling supports it, and your CI/CD pipelines work without changes. The only risk is keeping the old .sln format — you’ll be fighting an uphill battle as the ecosystem moves forward.
If you found this helpful, share it with your team — especially that developer who always gets stuck resolving .sln merge conflicts. They’ll thank you.
Happy Coding :)


