We're thrilled that you're interested in contributing to NiceGUI! Here are some guidelines that will help you get started.
NiceGUI is a Python library for building web-based user interfaces with minimal code. It's designed to be simple, powerful, and fun to use.
nicegui/- Core library code (public API)nicegui/elements/- Built-in UI elementsnicegui/functions/- Utility functionsexamples/- Standalone example applicationswebsite/- Documentation site (nicegui.io)tests/- Test suitemain.py- Runs the documentation website locally
- Python 3.9+ - Core language
- FastAPI/Starlette - Web framework
- Vue 3 - Frontend framework
- Quasar - UI component framework
- Tailwind CSS 4 - Styling
- pytest - Testing framework
If you encounter a bug or other issue with NiceGUI, the best way to report it is by opening a new issue on our GitHub repository. When creating the issue, please provide a clear and concise description of the problem, including any relevant error messages and code snippets. If possible, include steps to reproduce the issue.
We follow a Code of Conduct to ensure that everyone who participates in the NiceGUI community feels welcome and safe. By participating, you agree to abide by its terms.
We are excited that you want to contribute code to NiceGUI. We're always looking for bug fixes, performance improvements, and new features.
This project is designed to work well with AI assistants like Cursor, GitHub Copilot, and others. See AGENTS.md for guidelines specifically for AI assistants that complement this contributing guide.
We provide review instructions for PR reviews in .github/copilot-instructions.md. You should review your changes with an AI assistant before committing/pushing:
In Cursor or VS Code with GitHub Copilot Chat:
Select Agent Mode with claude-4.5-sonnet and write: Review my current branch according to @.github/copilot-instructions.md
Tip
In Cursor, you can use these custom commands for easy access:
/review-uncommitted- Review your local uncommitted changes/review-changes- Review your current branch vs main
Ensure to address any valid feedback. That will make your life and that of the maintainers much easier.
The simplest way to setup a fully functioning development environment is to start our Dev Container in VS Code:
- Ensure you have VS Code, Docker and the Dev Containers extension installed.
- Open the project root directory in VS Code.
- Press
F1, typeDev Containers: Open Folder in Container, and hit enter (or use the bottom-left corner icon in VS Code to reopen in container). - Wait until image has been build.
- Happy coding.
To set up a local development environment for NiceGUI, you'll need to have Python 3.9+ and uv installed.
You can install uv using:
curl -LsSf https://astral.sh/uv/install.sh | shThen use the following command to install NiceGUI in editable mode with all dependencies:
uv syncThis will install the nicegui package and all its dependencies, and link it to your local development environment so that changes you make to the code will be immediately reflected.
Thereby enabling you to use your local version of NiceGUI in other projects.
To run the tests you need some additional setup which is described in tests/README.md.
There is no special Python version required for development. At Zauberzeug we mainly use 3.12. This means we sometimes miss some incompatibilities with older versions. But these will hopefully be uncovered by the GitHub Actions (see below). Also we use the 3.9 Docker container described below to verify compatibility in cases of uncertainty.
You can also use Docker for development by starting the development container using the command:
./docker.sh up appBy default, the development server listens to http://localhost:80/.
The configuration is written in the docker-compose.yml file and automatically loads the main.py which contains the website https://nicegui.io.
Every code change will result in reloading the content.
We use Python 3.9 as a base to ensure compatibility (see development.dockerfile).
To view the log output, use the command
./docker.sh logThis section covers our formatting conventions, style principles, workflow practices, and automated linting enforcement.
We follow PEP 8 with a few deviations (see Style Principles below).
We use autopep8 with a 120 character line length to format our code. Before submitting a pull request, please run
uv run autopep8 --max-line-length=120 --in-place --recursive .on your code to ensure that it meets our formatting guidelines. Alternatively you can use VSCode, open the nicegui.code-workspace file and install the recommended extensions. Then the formatting rules are applied whenever you save a file.
In our point of view, the Black formatter is sometimes a bit too strict. There are cases where one or the other arrangement of, e.g., function arguments is more readable than the other. Then we like the flexibility to either put all arguments on separate lines or only put the lengthy event handler on a second line and leave the other arguments as they are.
Fluent Interface Formatting: When chaining methods (builder pattern), use backslash line continuation:
ui.button('Click me') \
.classes('bg-green') \
.on('click', lambda: ui.notify('Hello'))- Always prefer simple solutions
- Avoid having files over 200-300 lines of code. Refactor at that point
- Use single quotes for strings in Python, double quotes in JavaScript
- Use
# NOTE:prefix for important implementation details and non-obvious code - Use f-strings wherever possible for better readability (except in performance-critical sections which should be marked with
# NOTE:comments) - Follow autopep8 formatting with 120 character line length
- Never use mutable defaults (
[],{}) without# noqa: B006and justification; preferNoneas default - Put high-level/interesting code at the top of files; helper functions should be below their usage
- Each sentence in documentation should be on a new line
- Ensure proper use of async (no blocking operations)
- Never use
asyncio.create_task(), because the garbage collector might remove unfinished tasks. Always usebackground_tasks.create()which takes better care of task lifecycle management.
- Always simplify the implementation as much as possible:
- Avoid duplication of code whenever possible, which means checking for other areas of the codebase that might already have similar code and functionality
- Remove obsolete code
- Ensure the code is not too complicated
- Strive to have minimal maintenance burden and self explanatory code without the need of additional comments
- Be careful to only make changes that are requested or are well understood and related to the change being requested
- When fixing an issue or bug, do not introduce a new pattern or technology without first exhausting all options for the existing implementation. And if you finally do this, make sure to remove the old implementation afterwards so we don't have duplicate logic
- Keep the codebase very clean and organized
- Create tests for new features and bugfixes: use
Screenfixture only when browser is involved (JavaScript etc.), otherwise theUserfixture should be preferred as it is much faster and simpler to use (test runs in same async context as NiceGUI) - Run tests before submitting any changes
- Format code using autopep8 before submitting changes
- Use pre-commit hooks to ensure coding style compliance
- When adding new features, include corresponding tests
- For documentation, ensure each sentence is on a new line
- Discuss before implementing and if an approach is unclear, present options and trade-offs
- Approach large changes step-by-step and get confirmation before drastic refactorings
- Think from first principles: Always question your assumptions to find the true nature of problems
We use pre-commit to make sure the coding style is enforced.
If you used uv sync to install the dependencies, pre-commit should already have been installed then.
Now run this command to install the git commit hooks used in this project:
uv run pre-commit installAfter that you can make sure your code satisfies the coding style by running the following command:
uv run pre-commit run --all-filesTip
The command may fail with
RuntimeError: failed to find interpreter for Builtin discover of python_spec='python3.9'
You will need to install Python 3.9 and make sure it is available in your PATH.
These checks will also run automatically before every commit:
- Run
uv run ruff check . --fixto check the code and sort imports. - Remove trailing whitespace.
- Fix end of files.
- Enforce single quotes.
Note
Regarding single or double quotes: > PEP 8 doesn't give any recommendation, so we simply chose single quotes and sticked with it. On qwerty keyboards it's a bit easier to type, is visually less cluttered, and it works well for strings containing double quotes from the English language.
Note
We use f-strings where ever possible because they are generally more readable - once you get used to them.
There are only a few places in the code base where performance really matters and f-strings might not be the best choice.
These places should be marked with a # NOTE: ... comment when diverging from f-string usage.
Beyond standard Python conventions, NiceGUI has developed specific architectural patterns to handle its unique requirements: real-time UI synchronization between Python and Vue.js, efficient memory management with many short-lived objects, and a fluent API design.
When contributing to the core library (nicegui/ directory), you'll encounter these patterns.
They may seem unusual at first, but they solve specific problems related to NiceGUI's architecture.
Understanding these patterns will help you write code that fits naturally into the existing codebase.
- Mixins: Elements use mixin composition; inheritance order matters for Python's Method Resolution Order (MRO)
- Props vs. Attributes: Use
self._propsfor data that syncs to Vue/frontend; use instance attributes for Python-only state - Context Managers: Elements can be used as context managers (
with element:) for slot/child management - Component Registration: Use class parameters for components:
component='file.js',esm={'package': 'dist'},default_classes='css-class'
- Weakref Pattern: Use
self._element = weakref.ref(element)to avoid circular references (which require more costly garbage collection). When dereferencing, always check forNone:element = self._element(); if element is not None: element.update() - WeakValueDictionary: Use for caches that shouldn't prevent garbage collection by means of the reference counting mechanism of CPython
- BindableProperty: Use with
cast(Self, sender)in on-change handlers for type safety - Triple Underscore Storage: BindableProperty stores values in
___property_nameattributes - Batch Updates: Use
suspend_updates()context manager when changing properties inupdate()method to avoid infinite cycles
- Use
helpers.is_coroutine_function()to check if a handler is async - Use
helpers.expects_arguments()to check if a handler expects arguments - Use
core.app.handle_exception()for handling exceptions in background tasks
- Methods that support chaining (fluent interface) return
Selffromtyping_extensions - This enables the builder pattern:
element.method1().method2().method3()
- Use assertions for internal invariants (e.g.,
assert self.current_scene is not None) - Raise descriptive exceptions (
ValueError,RuntimeError) with helpful messages - Use
core.app.handle_exception()for global exception handling in background tasks
- Use
@dataclass(**KWONLY_SLOTS)for Python 3.9 compatibility (instead of@dataclass(kw_only=True, slots=True)) - This pattern is defined in
nicegui/dataclasses.pyand handles version differences automatically
Our tests are built with pytest and require python-selenium with ChromeDriver. See tests/README.md for detailed installation instructions and more infos about the test infrastructure and tricks for daily usage.
- Use
Userfixture when possible: Fast, runs in the same async context as NiceGUI, no browser needed - Use
Screenfixture only when necessary: Required when testing browser/JavaScript interactions, but slower
Before submitting a pull request, please make sure that all tests are passing. To run them all, use the following command in the root directory of NiceGUI:
uv run pytestIf you plan to implement a new element you can follow these suggestions:
- Ensure with the maintainers that the element is a good fit for NiceGUI core; otherwise it may be better to create a separate git repository for it.
- Clone the NiceGUI repository and install the requirements including the project itself with
uv sync. - Launch
main.pyin the root directory:uv run main.py. - Create a
test.pyfile or similar where you can experiment with your new element. - Look at other similar elements and how they are implemented in
nicegui/elements. - Create a new file with your new element alongside the existing ones.
- Make sure your element works as expected.
- Add a documentation file in
website/documentation/content. By calling the@doc.demo(...)function with an element as a parameter the docstring is used as a description. The docstrings are written in restructured-text. Make sure to use the correct syntax (like double backticks for code). Refer to the new documentation page using@doc.intro(...)in any documentation sectionwebsite/documentation/content/section_*.py. - Create a pull-request (see below).
There is a separate page for each element where multiple interactive demos can be listed. Please help us grow the number of insightful demos by following these easy steps:
- Clone the NiceGUI repository and install the requirements including the project itself with
uv sync. - Launch
main.pyin the root directory withuv run main.py. - In the newly opened browser window you can navigate to the documentation page where you want to change something.
- Open the code in your editor (for example website/documentation/content/table_documentation.py).
- In the
more()function insert an inner function containing your demo code. - Add the
@text_demodecorator to explain the demo. - Make sure the result looks as expected in the rendered documentation.
- Create a pull-request (see below).
Your contributions are much appreciated.
Because it has numerous benefits we write each sentence in a new line. Use backslash at end of line for line breaks without paragraph breaks.
Besides the documentation with interactive demos (see above) we collect useful, compact stand-alone examples.
Each example should be about one concept.
Please try to make them as minimal as possible to show what is needed to get some kind of functionality.
We are happy to merge pull requests with new examples which show new concepts, ideas or interesting use cases.
To list your addition on the website itself, you can use the example_link function below the
"In-depth examples" section heading.
The title should match the example folder name when snake case converted.
We use package.json files to pin the versions of node dependencies.
There is a package.json file in the root directory for core dependencies
and additional package.json files in nicegui/elements/.../ directories for individual UI elements.
They are usually updated by the maintainers during major releases.
To update or add new dependencies, we follow these steps:
- Use
npmor other derivative tools, modify thepackage.jsonfile with new versions or add dependencies. - Run
npm installto install the new dependencies. Any conflicts in installation will be caught at this moment. - Run
npm run buildto copy the dependencies into thenicegui/static/directory or to bundle the dependencies in thenicegui/elements/.../directories.
The following tools are used to update other resources:
- fetch_google_fonts.py for fetching the Google Fonts
- fetch_languages.py to update the list of supported languages in language.py
- fetch_milestone.py to prepare the release notes for a given milestone
- fetch_sponsors.py to update the list of sponsors on the website and in the README.md file
- summarize_dependencies.py to update the dependencies in the DEPENDENCIES.md file
To get started:
- Fork the repository on GitHub
- Clone your fork to your local filesystem
- Create a feature branch (e.g.,
git checkout -b fix-button-alignmentorgit checkout -b add-dark-mode) - Make your changes, commit them to your branch
- Push your feature branch to your fork (e.g.,
git push origin fix-button-alignment) - Open a pull request (PR) from your feature branch with a detailed description of your changes (the PR button is shown on the GitHub website of your forked repository)
Important
Always work on a feature branch, never on main:
- Working on
mainblocks you from contributing to multiple things simultaneously, - It highly increases the chance of an unclean commit history,
- Your
mainbranch should stay in sync with the upstream repository.
When submitting a PR, please make sure that the code follows the existing coding style and that all tests are passing. If you're adding a new feature, please include tests that cover the new functionality.
We welcome and support video and tutorial contributions to the NiceGUI community! As recently highlighted in a conversation on YouTube, creating and sharing tutorials or showcasing projects using NiceGUI can be an excellent way to help others learn and grow, while also spreading the word about our library.
Please note that NiceGUI is pronounced like "nice guy," which might be helpful to know when creating any video content.
If you decide to create YouTube content around NiceGUI, we kindly ask that you credit our repository, our YouTube channel, and any relevant videos or resources within the description. By doing so, you'll be contributing to the growth of our community and helping us receive more amazing pull requests and feature suggestions.
We're thrilled to see your creations and look forward to watching your videos. Happy video-making!
Thank you for your interest in contributing to NiceGUI! We're looking forward to working with you!