The gro library is a wrapper around the excellent Gradio library that simplifies the creation of any persistent single-user, multi-page UI application. It provides a way to separate the UI from the logic, making it easier to maintain and test the application.
As gradio is designed to be very easy to use for the input-output applications, its flexibility allows even more complex applications to be built on top of it. However, the complexity of the application can grow quickly as the number of UI elements and their interactions increase. The gro library aims to provide a structure to manage this complexity by introducing a clear separation between the UI and the logic.
It is designed to be used for applications that require a persistent state, such as a todo list, a note-taking app, or any other application that requires a multi-page UI. The library provides a way to define the UI elements and their layout, and then bind them to the logic of the application.
Use pip:
pip install git+https://github.com/aliakyurek/gro.gitSuppose you want to build a simple todo application with an authentication page and a home page that displays the todo list. The application will have the following features:
- An authentication page with a password input and a login button.
- A home page with a text input for adding new todo items, a button to add the item, and a markdown component to display the list of todo items.
- A top bar with a title and a logout button.
To build this application using the gro library, you will need to follow these steps:
- Inherit from the
gro.Pagefor each sub page of the application - Define the gradio UI components in the class members. Define the layout of the UI components in the
layout_elementsoverridden method. - Inherit from the
gro.Appclass to create the top UI that contains the pages and the top bar. - Here is the code for these all steps:
| Auth Page | HomePage | |
|---|---|---|
![]() |
![]() |
![]() |
Implement the model. This is where the logic of the application resides and independent from the gradio UI. It can be a simple class that holds the state of the application and provides methods to manipulate it. For example, a simple todo application model can look like this:
class Todo:
def __init__(self):
self.tasks = []
self.logged_in = False
def add_task(self, task):
self.tasks.append({"task": task, "completed": False})
def get_task_markdown(self):
return "\n".join(f"- {t['task']}" for t in self.tasks if not t["completed"])Now outline the application. This is where the UI and model are tied together.
-
Instantiate the model class.
-
Instantiate the UI from
UIAppand model fromTodoclasses.- Parameters passed to the
UIAppare passed to the Gradio Block constructor under the hood.
- Parameters passed to the
-
Implement event listeners in outside the UI class and bind them to the UI elements.
-
Attach event listeners. under a
with self.ui.block:context- See
self.ui.add_button.clickin theApplicationclass example. - Multiple event handlers are supported that utilizes
thenmethod of Gradio code. - See how multiple listeners binded for buttons.
- See
-
When the page is refreshed or closed/reopend all the state is normally lost if a Gradio state structure is not used. As we operate with a model, we can persist the state in the model and update UI from it. This is done by binding so called a data source function from model (or application) to a UI element.
- Binding can be used for Pages and any gradio UI element.
- The binder function is called when the UI element is to be rendered.
- The return value of the binder function is a dictionary of parameters which are valid for that UI element like parameters of gr.Button.
- Check
self.ui.logout_button.bindto see how the logout button visbility depends on the logged_in state of the model. - Pages can be also bound to a binder function. This is useful for showing/hiding pages based on the model state.
- For binder functions to be called, UI element must be rendered, and for UI element to be rendered, page must be refreshed.
- Therefore, when the login and logout buttons are clicked, as a post action, the UI is reloaded by calling
gro.App.RELOADwhich is a special dictionary that can be passed to thethenorsuccessmethods of Gradio event listeners. - The default password is
123and the application will show an error message if the password is incorrect, otherwise it changes the logged_in state of the model toTrueand shows the home page.
-
Finally call the
start()method of the UI to start the application. This function is blocking and will return when the application is closed.- As the HelloBlock utilizes Gradio Block under the hood,
launchparameters can be passed to UI constructor.
- As the HelloBlock utilizes Gradio Block under the hood,
class Application:
def __init__(self):
self.todo = Todo()
# ui configuration
self.ui = UIApp(title="gro Todo App")
# populate some initial data for model
self.todo.add_task("Wake up at 8. (Default task)")
# ui <-> model configuration
# set custom data bind, gradio event listeners
with self.ui.block:
# set custom data bind
# set custom event listeners
self.ui.auth_page.bind(lambda: { "visible": not self.todo.logged_in })
self.ui.home_page.bind(lambda: { "visible": self.todo.logged_in })
self.ui.home_page.tasks_markdown.bind(lambda: { "value": self.todo.get_task_markdown() }) # type: ignore
self.ui.logout_button.bind(lambda: { "visible": self.todo.logged_in }) # type: ignore
# set gradio event listeners
self.ui.auth_page.login_button.click(fn=self.login_button_click,
inputs=[self.ui.auth_page.password_textbox], queue=False).success(**gro.App.RELOAD)
self.ui.logout_button.click(fn=self.logout_button_click, queue=False).then(**gro.App.RELOAD)
self.ui.home_page.add_button.click(fn=self.add_button_click, inputs=[self.ui.home_page.todo_textbox],
outputs=[self.ui.home_page.tasks_markdown])
self.ui.home_page.add_button.click(fn=self.add_button_click_then, outputs=[self.ui.home_page.todo_textbox], show_progress="hidden")
def login_button_click(self, password):
self.todo.logged_in = password =="123"
# raise an error if password is not correct
if not self.todo.logged_in:
raise gr.Error("Incorrect password. Please try again.")
def logout_button_click(self):
self.todo.logged_in = False
def add_button_click(self, task):
self.todo.add_task(task)
return self.todo.get_task_markdown()
def add_button_click_then(self):
return ""
def run(self):
# following should be the last call as it doesn't return.
self.ui.launch(inbrowser=True)
if __name__ == "__main__":
app = Application()
app.run()- Clone the repository
git clone https://github.com/aliakyurek/gro.git - Change directory to the cloned repository
cd gro - Create a virtual environment
python -m venv venv - Activate the virtual environment
- On Windows:
venv\Scripts\activate - On Linux/Mac:
source venv/bin/activate
- On Windows:
- Install the library in editable mode
pip install -e .
This project is licensed under the MIT License.



