Why Should I Choose pyproject.toml over requirements.txt for managing dependencies?
requirements.txt lists packages to install. pyproject.tomlThe standard configuration file for Python projects. Declares the project name, version, dependencies, build system, and tool settings in one place. Learn more → defines a project: its dependenciesAn external package your project needs, listed in pyproject.toml so tools can install it automatically. , the Python version it requires, its metadata, and the tools that build and check it. That distinction shapes how dependencies get managed, how environments get reproduced, and how projects scale.
For new projects, use pyproject.toml. The rest of this page explains why, compares the two formats side by side, and covers the cases where requirements.txt still makes sense.
Compare the two formats
| requirements.txt | pyproject.toml | |
|---|---|---|
| Format | Plain text, one package per line | TOML with standardized sections |
| Dependencies | Flat list | Structured [project.dependencies] table |
| Python version constraint | None | requires-python field |
| Dev/test separation | Ad-hoc files (requirements-dev.txt) | Dependency groups |
| Project metadata | Lives elsewhere (setup.py, setup.cfg) | Built in: name, version, authors, license |
| Tool configuration | Separate config files per tool | [tool.*] tables in the same file |
| LockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs. support | Manual pinning or pip-compile | uv lock / uv sync from the same file |
| Reproducibility | Depends on discipline | lockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs. tracks exact versions and hashes |
| Standardization | Informal convention | PEP 621 specification |
Where requirements.txt still wins
The format is plain text, one package per line:
requests>=2.28.0
pandas~=2.0.0Any tool that speaks pip can consume it. For a script that only needs requests, a one-line text file is the right tool.
Where requirements.txt breaks down
The workflow around requirements.txt demands manual coordination. A developer creates a virtual environmentAn isolated folder where Python installs packages for one project, keeping them separate from other projects and your system Python. Learn more → , activates it, installs packages with pip, then remembers to update the file by hand. Each step is a chance for the file and the environment to drift apart.
The format also has structural gaps:
- No Python version constraint. Nothing stops installation into an incompatible interpreterThe program that reads and executes Python code. When you run "python3 hello.py", python3 is the interpreter. .
- No dev/prod separation. Splitting dependencies requires multiple files (requirements-dev.txt, requirements-test.txt) with no standard convention.
- No project metadata. Name, version, authors, and build configuration live elsewhere (setup.py, setup.cfg), spreading a project’s identity across files.
Tools like pip-tools solve some of these problems: pip-compile generates pinned requirements from abstract dependencies, adding lockfile-like reproducibility. pip-tools improves the workflow without fixing the format’s structural gaps: no metadata, no Python version constraints, no dev/test separation.
How pyproject.toml consolidates the workflow
PEP 621 standardized project metadata in pyproject.toml. Dependencies, Python version constraints, project metadata, and tool configuration all live in one file. Tools like flit, hatch, pdm, poetry, and uv (recommended) all read it.
With uv, creating a project and adding dependencies collapses to two commands:
uv init myproject && cd myproject
uv add requests pandasuv add creates the virtual environmentAn isolated folder where Python installs packages for one project, keeping them separate from other projects and your system Python.
Learn more →
, installs packages, and records them in pyproject.toml. When another developer clones the repo, uv sync reproduces the environment from the lockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs.
.
Replace scattered config with one file
pyproject.toml replaces the constellation of setup.py, setup.cfg, tox.ini, and requirements files with a single source of truth. The same file works whether the project is an application or an installable library. Dev dependenciesA package needed during development (testing, linting, formatting) that is not shipped to users of your project. Installed with the --dev flag. and dependency groups live alongside production dependencies, governed by a standard rather than ad-hoc file naming.
Bridge the gap with uv export
Switching to pyproject.toml does not mean abandoning every tool that reads requirements.txt. uv export generates a requirements.txt from pyproject.toml and its lockfileA file that records the exact version of every installed package, so everyone working on the project gets identical installs.
:
uv export --format requirements-txt > requirements.txtThis covers deployment targets that expect requirements.txt: Docker images, cloud functions (AWS Lambda, Google Cloud Run), and CI caches. The source of truth stays in pyproject.toml; the requirements.txt becomes a generated artifact.
Tip
How to use uv in a Dockerfile and Build a production Docker image for a uv project show how to install directly from pyproject.toml inside Docker without generating requirements.txt at all.
Where does setup.py fit?
setup.py was the original way to declare project metadata and build instructions. PEP 621 moved that metadata into pyproject.toml, making setup.py unnecessary for new projects. Existing setup.py projects can migrate with How to migrate from setup.py to pyproject.toml.
Start new projects with pyproject.toml
Use pyproject.toml for new projects. Keep requirements.txt for single-file scripts where a full project structure is unnecessary, or as a generated deployment artifact via uv export.
Pick your next step
- Starting fresh? Create your first Python project walks through
uv init, adding dependencies, and running code from scratch. - Have a requirements.txt project? How to migrate from requirements.txt to pyproject.toml with uv converts it step by step.
- Building a web app? Set up FastAPI or Django with uv and pyproject.toml.
- Deploying to production? Build a production Docker image for a uv project covers multi-stage builds with pyproject.toml.
- Want the full setup? Set up a complete Python project adds linting, formatting, type checking, and testing on top of pyproject.toml.
This handbook is free, independent, and ad-free. If it saved you time, consider sponsoring it on GitHub.