I am updating some NuGet packages to support .NET 10 and had to refresh my memory on how to multi-target NuGet packages that support multiple target frameworks. In my case, I am adding support for .NET 9 and .NET 10, but these steps are applicable to other .NET versions. I just decided to write it down in this article, so I can easily pull it up next time I need it.
1. Multi-target your project
In your .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Ensure you use <TargetFrameworks> in your csproj file, and don’t try to add multiple versions to <TargetFramework> (this is the default in your csproj if you only support one .NET when you ran them library project template). Trust me, I’ve hit this multiple times…
Then build and pack your project:
dotnet pack
either from the command line or in Visual Studio.
The build will produce the following with the NuGet package (nupkg file):
lib/
net9.0/
YourLib.dll
net10.0/
YourLib.dll
Developers will then consume the version that is appropriate for the project version that they included your package in. This is handled by the NuGet package manager and the appropriate target framework version.
2. Handling API differences (when needed)
If your code needs to diverge between .NET 9 and 10, use the target framework constants:
#if NET10_0
// .NET 10-only API
#else
// .NET 9 fallback
#endif
3. Using new .NET 10 features safely
With the #if for framework version shown above, we can now use new .NET 10 features when it makes sense, but still support older versions of .NET. There are a couple of options for doing that…
Option A: Put .NET 10-only code behind guards
public static class ExceptionExtensions
{
#if NET10_0
extension(SomeType)
{
public static void NewExtension() { }
}
#else
public static void ExtensionMethod(this Exception ex) { }
#endif
}
Option B: Split files per Target Version
In this option rather than combining all code into one file, you can split it between different source files for each version.
MyFeature.net10.cs
MyFeature.net9.cs
And then guard on version within the full file:
#if NET10_0
public class Foo
{
}
#endif
Option B is better when there are wholesale changes in the source file and they are very different between versions. This keeps the changes isolated, you don’t have to scatter a lot of #if statements throughout your file.
If you only need one or two small changes in a source file to support a newer .NET version, then option A is probably the best choice.
4. Package references that vary by version
If you require different dependencies for the different .NET versions, then you can add conditional package references to your csproj file:
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageReference Include="Some.Net10.Only.Package" Version="1.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Some.Legacy.Package" Version="2.5.0" />
</ItemGroup>
NuGet will pick the right package version to use in each option.
Note: if you are using packages that target both .NET 9 and .NET 10 (like most of the NuGet packages for .NET itself), then you don’t need this type of conditional references. NuGet will already handle using the appropriate version on its own.
5. Verify your package before publishing
Always inspect that the output contains both versions of your library by unzipping the nukpg file produced by the build (in the bin\Debug folder):
unzip YourLib.1.0.0.nupkg
You should see the following structure within the nupkg:
lib/net9.0lib/net10.0
with your package assemblies within each folder (targeting different versions).
6. Versioning guidance
You do not need separate NuGet package versions for .NET 9 vs 10. Both .NET targets are in a single NuGet package. So there is only one, single version for both .NET targets. If you are adding breaking changes then you should reversion the new package appropriately. And if you remove support for an older .NET version (like .NET 7-8), then you should change your package’s major version number because that would be considered a breaking change.
Conclusion
Those are the steps that you need to undertake to ensure that your NuGet package supports multiple .NET versions. It’s pretty straightforward to build this into your library and keeps your developers happy while they are also transitioning to new versions of .NET.