Target frameworks in SDK-style projects

When you target a framework in an app or library, you're specifying the set of APIs that you'd like to make available to the app or library. You specify the target framework in your project file using a target framework moniker (TFM).

An app or library can target a version of .NET Standard. .NET Standard versions represent standardized sets of APIs across all .NET implementations. For example, a library can target .NET Standard 1.6 and gain access to APIs that function across .NET Core and .NET Framework using the same codebase.

An app or library can also target a specific .NET implementation to gain access to implementation-specific APIs. For example, an app that targets Xamarin.iOS (for example, Xamarin.iOS10) has access to Xamarin-provided iOS API wrappers for iOS 10, or an app that targets Universal Windows Platform (UWP, uap10.0) has access to APIs that compile for devices that run Windows 10.

For some target frameworks, such as .NET Framework, the APIs are defined by the assemblies that the framework installs on a system and may include application framework APIs (for example, ASP.NET).

For package-based target frameworks (for example, .NET 5+, .NET Core, and .NET Standard), the APIs are defined by the NuGet packages included in the app or library.

Latest versions

The following table defines the most common target frameworks, how they're referenced, and which version of .NET Standard they implement. These target framework versions are the latest stable versions. Prerelease versions aren't shown. A target framework moniker (TFM) is a standardized token format for specifying the target framework of a .NET app or library.

Target framework Latest
stable version
Target framework moniker (TFM) Implemented
.NET Standard version
.NET 6 6 net6.0 N/A
.NET 5 5 net5.0 N/A
.NET Standard 2.1 netstandard2.1 N/A
.NET Core 3.1 netcoreapp3.1 2.1
.NET Framework 4.8 net48 2.0

Supported target frameworks

A target framework is typically referenced by a TFM. The following table shows the target frameworks supported by the .NET SDK and the NuGet client. Equivalents are shown within brackets. For example, win81 is an equivalent TFM to netcore451.

Target Framework TFM
.NET 5+ (and .NET Core) netcoreapp1.0
netcoreapp1.1
netcoreapp2.0
netcoreapp2.1
netcoreapp2.2
netcoreapp3.0
netcoreapp3.1
net5.0*
net6.0*
.NET Standard netstandard1.0
netstandard1.1
netstandard1.2
netstandard1.3
netstandard1.4
netstandard1.5
netstandard1.6
netstandard2.0
netstandard2.1
.NET Framework net11
net20
net35
net40
net403
net45
net451
net452
net46
net461
net462
net47
net471
net472
net48
Windows Store netcore [netcore45]
netcore45 [win] [win8]
netcore451 [win81]
.NET Micro Framework netmf
Silverlight sl4
sl5
Windows Phone wp [wp7]
wp7
wp75
wp8
wp81
wpa81
Universal Windows Platform uap [uap10.0]
uap10.0 [win10] [netcore50]

* .NET 5 and later TFMs include some operating system-specific variations. For more information, see the following section, .NET 5+ OS-specific TFMs.

.NET 5+ OS-specific TFMs

The net5.0 and net6.0 TFMs include technologies that work across different platforms. Specifying an OS-specific TFM makes APIs that are specific to an operating system available to your app, for example, Windows Forms or iOS bindings. OS-specific TFMs also inherit every API available to their base TFM, for example, the net5.0 TFM.

.NET 5 introduced the net5.0-windows OS-specific TFM, which includes Windows-specific bindings for WinForms, WPF, and UWP APIs. .NET 6 introduces further OS-specific TFMs.

The following table shows the compatibility of the .NET 5+ TFMs.

TFM Compatible with
net5.0 net1..4 (with NU1701 warning)
netcoreapp1..3.1 (warning when WinForms or WPF is referenced)
netstandard1..2.1
net5.0-windows netcoreapp1..3.1 (plus everything else inherited from net5.0)
net6.0 (subsequent version of net5.0)
net6.0-android xamarin.android (+everything else inherited from net6.0)
net6.0-ios xamarin.ios (+everything else inherited from net6.0)
net6.0-macos xamarin.mac (+everything else inherited from net6.0)
net6.0-maccatalyst xamarin.ios (+everything else inherited from net6.0)
net6.0-tvos xamarin.tvos (+everything else inherited from net6.0)
net6.0-windows (subsequent version of net5.0-windows)

To make your app portable across different platforms but still have access to OS-specific APIs, you can target multiple OS-specific TFMs and add platform guards around OS-specific API calls using #if preprocessor directives.

Suggested targets

Use these guidelines to determine which TFM to use in your app:

  • Apps that are portable to multiple platforms should target a base TFM, for example, net5.0. This includes most libraries but also ASP.NET Core and Entity Framework.

  • Platform-specific libraries should target platform-specific flavors. For example, WinForms and WPF projects should target net5.0-windows or net6.0-windows.

  • Cross-platform application models (Xamarin Forms, ASP.NET Core) and bridge packs (Xamarin Essentials) should at least target the base TFM, for example, net6.0, but might also target additional platform-specific flavors to light-up more APIs or features.

OS version in TFMs

You can also specify an optional OS version at the end of an OS-specific TFM, for example, net6.0-ios15.0. The version indicates which APIs are available to your app or library. It does not control the OS version that your app or library supports at run time. It's used to select the reference assemblies that your project compiles against, and to select assets from NuGet packages. Think of this version as the "platform version" or "OS API version" to disambiguate it from the run-time OS version.

When an OS-specific TFM doesn't specify the platform version explicitly, it has an implied value that can be inferred from the base TFM and platform name. For example, the default platform value for iOS in .NET 6 is 15.0, which means that net6.0-ios is shorthand for the canonical net6.0-ios15.0 TFM. The implied platform version for a newer base TFM may be higher, for example, a future net7.0-ios TFM could map to net7.0-ios16.0. The shorthand form is intended for use in project files only, and is expanded to the canonical form by the .NET SDK's MSBuild targets before being passed to other tools, such as NuGet.

The .NET SDK is designed to be able to support newly released APIs for an individual platform without a new version of the base TFM. This enables you to access platform-specific functionality without waiting for a major release of .NET. You can gain access to these newly released APIs by incrementing the platform version in the TFM. For example, if the iOS platform added iOS 15.1 APIs in a .NET 6.0.x SDK update, you could access them by using the TFM net6.0-ios15.1.

Support older OS versions

Although a platform-specific app or library is compiled against APIs from a specific version of that OS, you can make it compatible with earlier OS versions by adding the SupportedOSPlatformVersion property to your project file. The SupportedOSPlatformVersion property indicates the minimum OS version required to run your app or library. If you don't explicitly specify this minimum run-time OS version in the project, it defaults to the platform version from the TFM.

For your app to run correctly on an older OS version, it can't call APIs that don't exist on that version of the OS. However, you can add guards around calls to newer APIs so they are only called when running on a version of the OS that supports them. This pattern allows you to design your app or library to support running on older OS versions while taking advantage of newer OS functionality when running on newer OS versions.

The SupportedOSPlatformVersion value (whether explicit or default) is used by the platform compatibility analyzer, which detects and warns about unguarded calls to newer APIs. It's burned into the project's compiled assembly as an UnsupportedOSPlatformAttribute assembly attribute, so that the platform compatibility analyzer can detect unguarded calls to that assembly's APIs from projects with a lower SupportedOSPlatformVersion value. On some platforms, the SupportedOSPlatformVersion value affects platform-specific app packaging and build processes, which is covered in the documentation for those platforms.

Here is an example excerpt of a project file that uses the TargetFramework and SupportedOSPlatformVersion MSBuild properties to specify that the app or library has access to iOS 15.0 APIs but supports running on iOS 13.0 and above:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0-ios15.0</TargetFramework>
    <SupportedOSPlatformVersion>13.0</SupportedOSPlatformVersion>
  </PropertyGroup>

  ...

</Project>

How to specify a target framework

Target frameworks are specified in a project file. When a single target framework is specified, use the TargetFramework element. The following console app project file demonstrates how to target .NET 5:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

When you specify multiple target frameworks, you may conditionally reference assemblies for each target framework. In your code, you can conditionally compile against those assemblies by using preprocessor symbols with if-then-else logic.

The following library project targets APIs of .NET Standard (netstandard1.4) and .NET Framework (net40 and net45). Use the plural TargetFrameworks element with multiple target frameworks. The Condition attributes include implementation-specific packages when the library is compiled for the two .NET Framework TFMs:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>netstandard1.4;net40;net45</TargetFrameworks>
  </PropertyGroup>

  <!-- Conditionally obtain references for the .NET Framework 4.0 target -->
  <ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
    <Reference Include="System.Net" />
  </ItemGroup>

  <!-- Conditionally obtain references for the .NET Framework 4.5 target -->
  <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Threading.Tasks" />
  </ItemGroup>

</Project>

Within your library or app, you write conditional code using preprocessor directives to compile for each target framework:

public class MyClass
{
    static void Main()
    {
#if NET40
        Console.WriteLine("Target framework: .NET Framework 4.0");
#elif NET45
        Console.WriteLine("Target framework: .NET Framework 4.5");
#else
        Console.WriteLine("Target framework: .NET Standard 1.4");
#endif
    }
}

The build system is aware of preprocessor symbols representing the target frameworks shown in the Supported target framework versions table when you're using SDK-style projects. When using a symbol that represents a .NET Standard, .NET Core, or .NET 5 TFM, replace dots and hyphens with an underscore and change lowercase letters to uppercase (for example, the symbol for netstandard1.4 is NETSTANDARD1_4).

The complete list of preprocessor symbols for .NET target frameworks is:

Target Frameworks Symbols Additional symbols available in .NET 5+ SDK
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20 NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER
.NET 5+ (and .NET Core) NET, NET6_0, NET6_0_ANDROID, NET6_0_IOS, NET6_0_MACOS, NET6_0_MACCATALYST, NET6_0_TVOS, NET6_0_WINDOWS, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0 NET6_0_OR_GREATER, NET6_0_ANDROID_OR_GREATER, NET6_0_IOS_OR_GREATER, NET6_0_MACOS_OR_GREATER, NET6_0_MACCATALYST_OR_GREATER, NET6_0_TVOS_OR_GREATER, NET6_0_WINDOWS_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER

Note

  • Versionless symbols are defined regardless of the version you're targeting.
  • Version-specific symbols are only defined for the version you're targeting.
  • The <framework>_OR_GREATER symbols are defined for the version you're targeting and all earlier versions. For example, if you're targeting .NET Framework 2.0, the following symbols are defined: NET_2_0, NET_2_0_OR_GREATER, NET_1_1_OR_GREATER, and NET_1_0_OR_GREATER.

Deprecated target frameworks

The following target frameworks are deprecated. Packages that target these target frameworks should migrate to the indicated replacements.

Deprecated TFM Replacement
aspnet50
aspnetcore50
dnxcore50
dnx
dnx45
dnx451
dnx452
netcoreapp
dotnet
dotnet50
dotnet51
dotnet52
dotnet53
dotnet54
dotnet55
dotnet56
netstandard
netcore50 uap10.0
win netcore45
win8 netcore45
win81 netcore451
win10 uap10.0
winrt netcore45

See also