If you have worked with different versions of .NET and Visual Studio, you may have seen two different ways of managing NuGet packages: PackageReference and packages.config.

Both approaches can manage NuGet dependencies, but they work differently and are associated with different project types and .NET development styles.

In this guide, we'll compare PackageReference and packages.config, explain how they work, and help you understand which approach is appropriate for your project.


๐Ÿ“ฆ What Is PackageReference?

PackageReference is a NuGet package management format where package dependencies are declared directly in the project file, usually the .csproj file.

For example:

<ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>

The package information is therefore part of the project definition.

PackageReference is commonly used by modern SDK-style .NET projects, including modern .NET applications.


๐Ÿ“„ What Is packages.config?

packages.config is an older NuGet package management format where package dependencies are stored in a separate XML file named packages.config.

A typical file might look like this:

<?xml version="1.0" encoding="utf-8"?>
<packages>
    <package id="Newtonsoft.Json"
             version="13.0.3"
             targetFramework="net48" />

    <package id="Example.Package"
             version="2.1.0"
             targetFramework="net48" />
</packages>

Instead of putting package references directly inside the project file, the package list is maintained separately.


โš”๏ธ PackageReference vs packages.config

PackageReference packages.config
Package information is stored in the project file Package information is stored in packages.config
Common in modern SDK-style projects Common in older .NET Framework projects
Supports transitive dependency management Uses the older package management model
Usually produces a simpler project structure Requires a separate packages.config file
Preferred for modern projects Still found in many legacy projects

๐Ÿ” How PackageReference Works

With PackageReference, the package dependency is declared directly inside the project file.

For example:

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

    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Newtonsoft.Json"
                          Version="13.0.3" />
    </ItemGroup>

</Project>

The project file contains both the project configuration and its direct package references.

This makes the dependency declaration easy to see and manage alongside the rest of the project configuration.


๐Ÿ“ How packages.config Works

With packages.config, the project and package list are separate.

The project may contain a reference to a package, while the actual package information is stored in:

packages.config

For example:

<packages>
    <package id="Newtonsoft.Json"
             version="13.0.3"
             targetFramework="net48" />
</packages>

This approach was widely used by older .NET Framework projects.


๐ŸŒณ Dependency Management Differences

One of the biggest differences is how dependencies are represented and resolved.

With PackageReference, NuGet can build a dependency graph containing direct and transitive dependencies.

Your Project
โ”‚
โ”œโ”€โ”€ PackageA       โ† Direct
โ”‚   โ””โ”€โ”€ PackageB   โ† Transitive
โ”‚       โ””โ”€โ”€ PackageC
โ”‚
โ””โ”€โ”€ PackageD       โ† Direct

This makes understanding transitive dependencies particularly important when troubleshooting modern .NET projects.

You can inspect them with:

dotnet list package --include-transitive

For a detailed explanation of direct and transitive dependencies, see:

๐Ÿ“ฆ Direct vs Transitive NuGet Dependencies: What's the Difference?


๐Ÿ’ป Which Project Types Use PackageReference?

PackageReference is commonly associated with modern SDK-style projects.

Examples include:

A quick way to recognize an SDK-style project is to see a project file beginning with:

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

๐Ÿ›๏ธ Which Projects Commonly Use packages.config?

packages.config is most commonly encountered in older .NET Framework projects.

For example, an older ASP.NET MVC or Windows Forms application may still contain:

packages.config

If you are maintaining an older application, there is no need to immediately change the package management format simply because PackageReference is newer.

First consider the project's framework, project format, dependencies, and migration requirements.


๐Ÿ”„ Can You Move from packages.config to PackageReference?

In many supported project scenarios, migration from packages.config to PackageReference is possible.

However, migration should be planned carefully, especially for older applications with many dependencies.

Before migrating, consider:


โš ๏ธ Why Migration Can Cause Problems

A legacy project may have packages that were designed around the older packages.config workflow.

During migration, you may encounter:

This is why migration should ideally be performed with source control enabled so that changes can be reviewed or reverted if necessary.


๐Ÿงฉ PackageReference and Transitive Dependencies

PackageReference makes transitive dependencies an important part of understanding your project's dependency graph.

Suppose your project directly references:

PackageA 5.0.0

Package A requires:

PackageB 3.0.0

Your project therefore receives Package B transitively.

YourProject
    โ”‚
    โ””โ”€โ”€ PackageA 5.0.0
            โ”‚
            โ””โ”€โ”€ PackageB 3.0.0

If another direct dependency requires a different version of Package B, NuGet has to resolve those version requirements.

This can result in dependency warnings or errors.


๐Ÿšจ PackageReference and NuGet Conflicts

When working with PackageReference, a dependency conflict can sometimes involve a package that isn't directly listed in your project file.

For example:

YourProject
โ”‚
โ”œโ”€โ”€ PackageA
โ”‚   โ””โ”€โ”€ CommonLibrary 1.x
โ”‚
โ””โ”€โ”€ PackageB
    โ””โ”€โ”€ CommonLibrary 2.x

Neither Package A nor Package B may appear to be the obvious source of the problem when you first see the error.

Inspecting the dependency tree can reveal the complete chain.

If you're experiencing a package version conflict, start here:

๐Ÿ“ฆ How to Fix NuGet Package Version Conflicts in Visual Studio


๐Ÿ”Ž How to Identify Your Project's Package Management Format

The easiest way is to inspect the project files.

If you see:

<PackageReference Include="Some.Package"
                  Version="1.0.0" />

your project is using PackageReference for that package.

If you see a file named:

packages.config

the project is using the packages.config format.


๐Ÿ“Š Which One Should You Use?

For a new modern .NET project, PackageReference is generally the preferred approach.

It integrates naturally with SDK-style projects and provides modern dependency management capabilities.

However, if you are maintaining an existing legacy project that uses packages.config, changing the package management format isn't automatically necessary.

The best choice depends on your project type and whether migration provides a meaningful benefit.


๐Ÿ› ๏ธ Practical Recommendation

Situation Recommended Approach
New modern .NET project PackageReference
Existing SDK-style project PackageReference
Older .NET Framework project Evaluate the existing setup before migrating
Legacy application that works correctly Don't migrate without a specific reason

โ“ Frequently Asked Questions

What is the difference between PackageReference and packages.config?

PackageReference stores package references directly in the project file, while packages.config stores package information in a separate packages.config file.

Is PackageReference newer than packages.config?

Yes. PackageReference is the modern NuGet package management approach commonly used by SDK-style projects, while packages.config is associated with the older project model.

Should I convert every packages.config project?

No. Migration should be considered based on the project type, package compatibility, target framework, and the benefits you expect from the migration.

Does PackageReference support transitive dependencies?

Yes. Understanding transitive dependencies is an important part of working with PackageReference-based projects.

Can packages.config cause NuGet dependency problems?

NuGet dependency problems can occur with either package management approach. The exact cause depends on the project's dependencies, package versions, framework, and package resolution requirements.


๐Ÿ“š Related NuGet Articles

๐Ÿ“ฆ How to Fix NuGet Package Version Conflicts in Visual Studio

๐Ÿ”ง How to Fix NU1107: Version Conflict Detected for NuGet Package

๐Ÿ” How to Check Which NuGet Package Is Causing a Dependency Conflict

๐Ÿ“ฆ Direct vs Transitive NuGet Dependencies: What's the Difference?

๐Ÿ”„ How to Update NuGet Packages Safely Without Breaking Your Project


๐ŸŽฏ Conclusion

PackageReference and packages.config are two different approaches to managing NuGet dependencies.

PackageReference integrates package references directly into the project file and is commonly used by modern SDK-style projects.

packages.config uses a separate XML file and is commonly encountered in older .NET Framework projects.

If you're creating a new modern .NET application, PackageReference is generally the natural choice. If you're maintaining an existing legacy project, however, there may be no reason to migrate unless you have a specific need.

๐Ÿ“ฆ Having a NuGet dependency conflict?

Continue with the main troubleshooting guide: How to Fix NuGet Package Version Conflicts in Visual Studio โ†’

๐Ÿค– AlgoLassi Assistant Have a question about this tutorial?

Ask AlgoLassi and get an answer plus the tutorials worth studying next.

Ask a question

๐Ÿ’ฌ Comments

Sign in with Google to publish immediately, or comment anonymously and wait for approval.

Comments will appear here when available.