# How to Add a Dependency to a uv Project

[uv](https://pydevtools.com/handbook/reference/uv.md) manages project dependencies through one command. `uv add` writes the package into your [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md), resolves a compatible version into the [lockfile](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md), and installs it into the project's [virtual environment](https://pydevtools.com/handbook/explanation/what-is-a-virtual-environment.md). [pip](https://pydevtools.com/handbook/reference/pip.md) splits those three steps across `pip install` and a hand edit of your project metadata.

These commands assume a uv project, meaning a directory with a `pyproject.toml`. Run `uv init` first to create one if you don't have it yet.

## Add a package

Name the package and uv does the rest:

```console
$ uv add httpx
Using CPython 3.12.10 interpreter at: /usr/local/bin/python3.12
Creating virtual environment at: .venv
Resolved 8 packages in 114ms
Prepared 7 packages in 51ms
Installed 7 packages in 18ms
 + anyio==4.14.2
 + certifi==2026.7.22
 + h11==0.16.0
 + httpcore==1.0.9
 + httpx==0.28.1
 + idna==3.18
 + typing-extensions==4.16.0
```

Three files change. pyproject.toml gains a line in its `dependencies` array (`"httpx>=0.28.1"`), uv.lock records the exact resolved version and its hashes, and the `.venv` directory receives the install. The first `uv add` in a fresh project also creates that `.venv`, which is why the `Creating virtual environment` line appears before anything installs. Anyone who clones the project and runs `uv sync` reproduces the same set of versions.

Add several at once by listing them:

```bash
uv add httpx rich
```

## Pin a version

Pass a [version specifier](https://pydevtools.com/handbook/explanation/what-is-a-version-specifier.md) in quotes so the shell doesn't read `>` as a redirect:

```bash
uv add 'httpx>=0.27,<1.0'
```

uv writes that range to pyproject.toml and installs the newest version inside it. Without a specifier, uv adds a lower bound at the version it resolved (`httpx>=0.28.1`), which lets future upgrades move forward freely. To later raise a version ceiling that is blocking an upgrade, see [how to upgrade a dependency past its version ceiling](https://pydevtools.com/handbook/how-to/how-to-upgrade-a-capped-dependency-with-uv.md).

## Add a development dependency

Tools you use while working on the project, but that its users don't need, belong in a [dependency group](https://pydevtools.com/handbook/explanation/understanding-dependency-groups-in-uv.md). Pass `--dev` to put a package in the `dev` group:

```bash
uv add --dev pytest
```

This writes to `[dependency-groups]` in pyproject.toml rather than the main `dependencies` array. The `dev` group installs when you run `uv sync` locally but is left out when someone installs your package as a dependency of theirs. Use `--group <name>` to target a named group other than `dev`.

## Add an optional dependency

An [optional dependency](https://pydevtools.com/handbook/explanation/what-are-optional-dependencies-and-dependency-groups.md) ships as an *extra* that users opt into (`pip install yourpackage[plot]`). Add one with `--optional` and the extra's name:

```bash
uv add --optional plot matplotlib
```

uv writes `matplotlib` under a `plot` key in `[project.optional-dependencies]`. Unlike a dev dependency, an extra is published with your package, so downstream users can request it.

## Add from Git, a URL, or a local path

uv resolves sources beyond the default package index. Point at a Git repository with the `package @ git+URL` form:

```bash
uv add "httpx @ git+https://github.com/encode/httpx"
```

Pin the reference with `--tag`, `--branch`, or `--rev` to avoid tracking a moving branch:

```bash
uv add "httpx @ git+https://github.com/encode/httpx" --tag 0.28.1
```

uv records the repository under a `[tool.uv.sources]` table in pyproject.toml and keeps the package name in your `dependencies` list, so the source and the requirement stay separate.

For a package you develop alongside this project, add it from a local path as an [editable install](https://pydevtools.com/handbook/explanation/what-is-an-editable-install.md) so your edits take effect without reinstalling:

```bash
uv add --editable ../my-library
```

## Remove a dependency

`uv remove` reverses `uv add`, updating the same three places:

```bash
uv remove httpx
```

It drops the package from pyproject.toml, re-resolves and rewrites uv.lock, and uninstalls it from the virtual environment along with any dependencies no longer needed.

## Learn More

- [Managing dependencies](https://docs.astral.sh/uv/concepts/projects/dependencies/): the uv reference for every `add` flag and source type.
- [uv Reference](https://pydevtools.com/handbook/reference/uv.md): the full command surface and configuration options.
- [What is a lockfile?](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md) explains what uv.lock records and why it matters.
- [How to pin dependencies with hashes in uv](https://pydevtools.com/handbook/how-to/how-to-pin-dependencies-with-hashes-in-uv.md) covers verifying downloads against recorded hashes.
- [How to translate pip commands to uv](https://pydevtools.com/handbook/how-to/how-to-translate-pip-commands-to-uv.md) maps `pip install` and friends to their uv equivalents.
