Skip to content

uv init: project types, flags, and examples

uv

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-project

The generated layout contains a pyproject.toml, .python-version, README.md, .gitignore, and a sample main.py:

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-lib

The 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.typed

uv 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-cli

This combines the src/ layout from --lib with a [project.scripts] table:

pyproject.toml
[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 --bare

No 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-project

uv 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.

Learn more

Last updated on

Please submit corrections and feedback...