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 9 9 net9.0 2.1
.NET 8 8 net8.0 2.1
.NET Standard 2.1 netstandard2.1 N/A
.NET Core 3.1 netcoreapp3.1 2.1
.NET Framework 4.8.1 net481 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*
net7.0*
net8.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
net481
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, net6.0, net7.0, net8.0, and net.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 net9.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 and later versions have additional OS-specific TFMs, for example, net6.0-ios.

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 (plus everything else inherited from net6.0)
net6.0-ios Everything inherited from net6.0
net6.0-maccatalyst Everything inherited from net6.0
net6.0-macos Everything inherited from net6.0
net6.0-tvos Everything inherited from net6.0
net6.0-windows (Subsequent version of net5.0-windows)
net7.0 (Subsequent version of net6.0)
net7.0-android (Subsequent version of net6.0-android)
net7.0-ios (Subsequent version of net6.0-ios)
net7.0-maccatalyst (Subsequent version of net6.0-maccatalyst)
net7.0-macos (Subsequent version of net6.0-macos)
net7.0-tizen tizen40 (plus everything else inherited from net7.0)
net7.0-tvos (Subsequent version of net6.0-tvos)
net7.0-windows (Subsequent version of net6.0-windows)
net8.0 (Subsequent version of net7.0)
net8.0-android (Subsequent version of net7.0-android)
net8.0-browser Everything inherited from net8.0
net8.0-ios (Subsequent version of net7.0-ios)
net8.0-maccatalyst (Subsequent version of net7.0-maccatalyst)
net8.0-macos (Subsequent version of net7.0-macos)
net8.0-tizen (Subsequent version of net7.0-tizen)
net8.0-tvos (Subsequent version of net7.0-tvos)
net8.0-windows (Subsequent version of net7.0-windows)
net9.0 (Subsequent version of net8.0)
net9.0-android (Subsequent version of net8.0-android)
net9.0-browser (Subsequent version of net8.0-browser)
net9.0-ios (Subsequent version of net8.0-ios)
net9.0-maccatalyst (Subsequent version of net8.0-maccatalyst)
net9.0-macos (Subsequent version of net8.0-macos)
net9.0-tizen (Subsequent version of net8.0-tizen)
net9.0-tvos (Subsequent version of net8.0-tvos)
net9.0-windows (Subsequent version of net8.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. For a list of the available symbols, see Preprocessor symbols.

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, net9.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 net9.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, net9.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 doesn't 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 Android in .NET 6 is 31.0, which means that net6.0-android is shorthand for the canonical net6.0-android31.0 TFM. The implied platform version for a newer base TFM may be higher, for example, a future net8.0-android TFM could map to net8.0-android34.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 following table shows the default target platform values (TPV) for each .NET release.

.NET version Android iOS Mac Catalyst macOS tvOS Tizen Windows
.NET 8 34.0 17.2 17.2 14.2 17.1 7.0 10.0
.NET 9 35.0 18.0 18.0 15.0 7.0 10.0

Note

On Apple platforms (iOS, macOS, tvOS, and Mac Catalyst) in .NET 8 and earlier, the default TPV is the latest supported version in the currently installed workload. That means that updating the iOS workload in .NET 8, for example, might result in a higher default TPV, if support for a new version of iOS has been added in that workload. In the preceding table, the default TPV is the one in the initial release for the stated .NET version.

Starting in .NET 9, this special behavior only applies to executable projects. The default TPV for library projects now stays the same for the entirety of a major .NET release, like all other platforms.

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 Android platform added API level 32 APIs in a .NET 6.0.x SDK update, you could access them by using the TFM net6.0-android32.0.

Precedence

If your app references a package that has multiple assets for different TFMs, the assets that are closer in version number are preferred. For example, if your app targets net6.0-ios and the package offers assets for net6.0 and net5.0-ios, the net6.0 assets are used. For more information, see Precedences.

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 9:

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

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.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
    }
}

Preprocessor symbols

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. To convert a .NET Standard, .NET Core, or .NET 5+ TFM to a preprocessor symbol, replace dots and hyphens with an underscore, and change lowercase letters to uppercase (for example, the symbol for netstandard1.4 is NETSTANDARD1_4).

You can disable generation of these symbols via the DisableImplicitFrameworkDefines property. For more information about this property, see DisableImplicitFrameworkDefines.

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

Target Frameworks Symbols Additional symbols
(available in .NET 5+ SDKs)
Platform symbols (available only
when you specify an OS-specific TFM)
.NET Framework NETFRAMEWORK, NET481, 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, NET9_0, NET8_0, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_0 NET8_0_OR_GREATER, NET7_0_OR_GREATER, NET6_0_OR_GREATER, NET5_0_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 ANDROID, BROWSER, IOS, MACCATALYST, MACOS, TVOS, WINDOWS,
[OS][version] (for example IOS15_1),
[OS][version]_OR_GREATER (for example IOS15_1_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: NET20, NET20_OR_GREATER, NET11_OR_GREATER, and NET10_OR_GREATER.
  • The NETSTANDARD<x>_<y>_OR_GREATER symbols are only defined for .NET Standard targets, and not for targets that implement .NET Standard, such as .NET Core and .NET Framework.
  • These are different from the target framework monikers (TFMs) used by the MSBuild TargetFramework property and NuGet.

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