Skip to content

uv init: project types, flags, and examples

uv

The uv command uv init creates a new Python project. It generates a pyproject.toml and the project scaffold; the virtual environment and lockfile are created lazily on the first uv run, uv sync, or uv add. Different flags produce different project layouts: packaged applications, reusable libraries, unpackaged scripts, or a minimal scaffold, each suited to a specific use case.

Note

uv 0.12.0 (July 2026) changed the default: uv init now produces a packaged src/ layout with uv_build and a console-script entry point. The previous flat main.py layout is available with --no-package.

Flag Build System Structure Entry Point Use Case
(default) / --app uv_build src/ Console script Apps, CLI tools
--package uv_build src/ Console script Same as the default since 0.12.0
--lib uv_build src/ None Reusable libraries
--no-package None Flat main.py Scripts, internal apps
--bare None Minimal None Custom projects

Common uv init commands

uv init my-project

The default form creates a new directory and a packaged application project inside it:

uv init my-project

The generated layout places source code under src/, includes a [build-system] table, and defines a console-script entry point:

my-project/
├── .gitignore
├── .python-version
├── pyproject.toml
├── README.md
└── src/
    └── my_project/
        └── __init__.py

The pyproject.toml wires up uv_build and maps the my-project command to the main function in my_project. uv fills authors from your git config (omitting it when git has no user configured) and sets requires-python from the interpreter uv itself discovers, which is not always the python3 on your PATH, or from the version passed via --python:

pyproject.toml
[project]
name = "my-project"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
authors = [
    { name = "Ada Lovelace", email = "ada@example.com" }
]
requires-python = ">=3.13"
dependencies = []

[project.scripts]
my-project = "my_project:main"

[build-system]
requires = ["uv_build>=0.12.0,<0.13.0"]
build-backend = "uv_build"

Run the project with uv run my-project. On first run, uv creates the .venv/ directory, installs the project in editable mode, and writes a uv.lock file pinning transitive dependencies.

To get the previous flat main.py layout without a build system, pass --no-package:

uv init --no-package my-project

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, which works with the default project type as well as --lib. The empty py.typed marker tells type checkers the package ships inline type information.

uv init --no-package

Use --no-package for scripts and internal applications that you never plan to distribute:

uv init --no-package my-script

This produces a flat layout with a main.py and no [build-system] table, the layout that uv init generated before version 0.12.0. Run the script with uv run main.py. Without a build system, uv does not treat it as a package and will not install it into the project environment; it is not intended for distribution to PyPI.

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:

Project structure. --app and --lib use a src/ layout that keeps source code separate from the project root. --no-package produces a flat layout with main.py at the root. --bare generates only a minimal pyproject.toml with no sample code or directories.

Build system. The --app and --lib projects include a [build-system] table using uv_build, which lets the project be installed as a package, imported from tests, and published to PyPI. --no-package and --bare omit this, since they are not intended for distribution.

Entry points. The default --app project adds a [project.scripts] entry that becomes a runnable command (uv run my-project). --lib omits the scripts entry because a library is imported, not executed. --no-package provides a main.py instead, run with uv run main.py.

Learn more

Last updated on