uv init: project types, flags, and examples
The uv command uv init creates a new Python project with a pyproject.toml, a virtual environment, and a lockfile. Different flags produce different project layouts — applications, reusable libraries, packageable CLI tools, or a minimal scaffold — each suited to a specific use case.
| Flag | Build System | Structure | Entry Point | Distribution | Use Case |
|---|---|---|---|---|---|
--app (default) |
No | Flat | main.py |
Not intended | Web apps, scripts |
--lib |
Yes | src/ layout |
None | PyPI packages | Reusable libraries |
--package |
Yes | src/ layout |
Console script | Installable apps | CLI tools |
--bare |
No | Minimal | None | Manual setup | Custom projects |
--no-package |
No | Flat | main.py |
Explicitly blocked | Internal apps |
Common uv init commands
uv init my-project
The default form creates a new directory and an application project inside it:
uv init my-projectThe generated layout contains a pyproject.toml, .python-version, README.md, .gitignore, and a sample main.py:
def main():
print("Hello from my-project!")
if __name__ == "__main__":
main()Run the script with uv run main.py. On first run, uv creates the .venv/ directory and writes a uv.lock file pinning transitive dependencies.
Running uv init with no arguments initializes a project in the current directory instead of creating a new one. This is the right choice when the directory already exists (for example, a freshly cloned git repository).
uv init --lib
Use --lib for libraries meant to be published to PyPI or imported by other projects:
uv init --lib my-libThe result uses the src/my_lib/ layout, which is the recommended structure for libraries because it forces imports to resolve against the installed package rather than the working directory:
my-lib/
├── .gitignore
├── .python-version
├── pyproject.toml
├── README.md
└── src/
└── my_lib/
├── __init__.py
└── py.typeduv also runs git init in the new project directory by default. Pass --vcs none to skip it.
The pyproject.toml includes a full [build-system] table using uv_build as the default build backend. To use a different backend — hatchling, flit-core, pdm-backend, setuptools, maturin, or scikit-build-core — pass --build-backend alongside --lib. The empty py.typed marker tells type checkers the package ships inline type information.
uv init --package
Use --package for applications that should be installable as distributable packages, such as command-line tools:
uv init --package my-cliThis combines the src/ layout from --lib with a [project.scripts] table:
[project.scripts]
my-cli = "my_cli:main"When the project is installed (for example with uv tool install . or pip install .), a my-cli executable appears on the user’s PATH and calls my_cli.main(). Use --package instead of --app when the CLI is meant to be installed rather than run out of the source directory.
uv init --bare
Use --bare when you only want a minimal pyproject.toml:
uv init --bareNo sample code, no README.md, no .python-version, no src/ directory — just a pyproject.toml with essential metadata. This is the right choice when adding uv to an existing project with its own structure, or when scripting project creation and you want to generate files yourself.
uv init with a specific Python version
Pass --python to pin the new project to a specific Python version:
uv init --python 3.12 my-projectuv writes 3.12 to .python-version and sets requires-python = ">=3.12" in pyproject.toml. If Python 3.12 is not already installed, uv downloads a managed build automatically on the next uv run or uv sync. The same flag works with any project type — uv init --lib --python 3.13 my-lib creates a library pinned to Python 3.13.
To change the Python version of an existing uv project instead, edit .python-version and requires-python, or use uv python pin 3.12.
How project types differ
The project types differ in three areas:
Build system. Libraries and packageable applications include a [build-system] table for creating wheels and source distributions. Applications and bare projects omit this because they are not meant to be packaged.
Project structure. Libraries and packageable applications use the src/ layout, which improves testing isolation by preventing accidental imports from the working directory. Plain applications use a flat structure for simplicity.
Entry points. Packageable applications define [project.scripts] entries that become executable commands when the package is installed. Plain applications rely on uv run main.py instead.