Quick overview

Here you can learn the most important things about LVGL. You should read it first to get a general impression and read the detailed Porting and Overview sections after that.

Get started in a simulator

Instead of porting LVGL to an embedded hardware, it's highly recommended to get started in a simulator first.

LVGL is ported to many IDEs to be sure you will find your faviourite one. Go to Simulators to get ready-to-use projects which can be run on your PC. This way you can save the porting for now and make some experience with LVGL immediately.

Add LVGL into your project

The following steps show how to setup LVGL on an embedded system with a display and a touchpad.

  • Download or Clone the library from GitHub with git clone https://github.com/lvgl/lvgl.git

  • Copy the lvgl folder into your project

  • Copy lvgl/lv_conf_template.h as lv_conf.h next to the lvgl folder, change the first #if 0 to 1 to enable the file's content and set at least LV_HOR_RES_MAX, LV_VER_RES_MAX and LV_COLOR_DEPTH defines.

  • Include lvgl/lvgl.h where you need to use LVGL related functions.

  • Call lv_tick_inc(x) every x milliseconds in a Timer or Task (x should be between 1 and 10). It is required for the internal timing of LVGL. Alternatively, configure LV_TICK_CUSTOM (see lv_conf.h) so that LVGL can retrieve the current time directly.

  • Call lv_init()

  • Create a display buffer for LVGL. LVGL will render the graphics here first, and seed the rendered image to the display. The buffer size can be set freely but 1/10 screen size is a good starting point.

static lv_disp_buf_t disp_buf;
static lv_color_t buf[LV_HOR_RES_MAX * LV_VER_RES_MAX / 10];                     /*Declare a buffer for 1/10 screen size*/
lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * LV_VER_RES_MAX / 10);    /*Initialize the display buffer*/
  • Implement and register a function which can copy the rendered image to an area of your display:

lv_disp_drv_t disp_drv;               /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv);          /*Basic initialization*/
disp_drv.flush_cb = my_disp_flush;    /*Set your driver function*/
disp_drv.buffer = &disp_buf;          /*Assign the buffer to the display*/
lv_disp_drv_register(&disp_drv);      /*Finally register the driver*/

void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            set_pixel(x, y, *color_p);  /* Put a pixel to the display.*/

    lv_disp_flush_ready(disp);         /* Indicate you are ready with the flushing*/
  • Implement and register a function which can read an input device. E.g. for a touch pad:

lv_indev_drv_t indev_drv;                  /*Descriptor of a input device driver*/
lv_indev_drv_init(&indev_drv);             /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/

bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
    data->state = touchpad_is_pressed() ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
    if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&data->point.x, &data->point.y);

    return false; /*Return `false` because we are not buffering and no more data to read*/
  • Call lv_task_handler() periodically every few milliseconds in the main while(1) loop, in Timer interrupt or in an Operation system task. It will redraw the screen if required, handle input devices etc.

For a more detailed guide go to the Porting section.

Learn the basics


The graphical elements like Buttons, Labels, Sliders, Charts etc are called objects or widgets in LVGL. Go to Widgets to see the full list of available widgets.

Every object has a parent object where it is create. For example if a label is created on a button, the button is the parent of label. The child object moves with the parent and if the parent is deleted the children will be deleted too.

Children can be visible only on their parent. It other words, the parts of the children out of the parent are clipped.

A screen is the "root" parent. You can have any number of screens. To get the current screen call lv_scr_act(), and to load a screen use lv_scr_load(scr1).

You can create a new object with lv_<type>_create(parent, obj_to_copy). It will return an lv_obj_t * variable which should be used as a reference to the object to set its parameters. The first parameter is the desired parent, the second parameters can be an object to copy (NULL if unused). For example:

lv_obj_t * slider1 = lv_slider_create(lv_scr_act(), NULL);

To set some basic attribute lv_obj_set_<paramters_name>(obj, <value>) function can be used. For example:

lv_obj_set_x(btn1, 30);
lv_obj_set_y(btn1, 10);
lv_obj_set_size(btn1, 200, 50);

The objects has type specific parameters too which can be set by lv_<type>_set_<paramters_name>(obj, <value>) functions. For example:

lv_slider_set_value(slider1, 70, LV_ANIM_ON);

To see the full API visit the documentation of the widgets or the related header file (e.g. lvgl/src/lv_widgets/lv_slider.h).


Events are used to inform the user if something has happened with an object. You can assign a callback to an object which will be called if the object is clicked, released, dragged, being deleted etc. It should look like this:

lv_obj_set_event_cb(btn, btn_event_cb);                 /*Assign a callback to the button*/


void btn_event_cb(lv_obj_t * btn, lv_event_t event)
    if(event == LV_EVENT_CLICKED) {

Learn more about the events in the Event overview section.


Widgets might be built from one or more parts. For example a button has only one part called LV_BTN_PART_MAIN. However, a Page has LV_PAGE_PART_BG, LV_PAGE_PART_SCROLLABLE, LV_PAGE_PART_SCROLLBAR and LV_PAGE_PART_EDGE_FLASG.

Some parts are virtual (they are not real object, just drawn on the fly, such as the scrollbar of a page) but other parts are real (they are real object, such as the scrollable part of the page).

Parts come into play when you want to set the styles and states of a given part of an object. (See below)


The objects can be in a combination of the following states:

  • LV_STATE_DEFAULT Normal, released

  • LV_STATE_CHECKED Toggled or checked

  • LV_STATE_FOCUSED Focused via keypad or encoder or clicked via touchpad/mouse

  • LV_STATE_EDITED Edit by an encoder

  • LV_STATE_HOVERED Hovered by mouse (not supported now)


  • LV_STATE_DISABLED Disabled or inactive

For example, if you press an object it will automatically get the LV_STATE_PRESSED state and when you release it, the state will be removed.

To get the current state use lv_obj_get_state(obj, part). It will return the ORed states. For example, this is a valid state for a checkbox: LV_STATE_CHECKED | LV_STATE_PRESSED | LV_STATE_FOCUSED


Styles can be assigned to the parts of an object to change their appearance. A style can describe for example the background color, border width, text font and so on. See the full list here.

The styles can be cascaded (similarly to CSS). It means you can add more styles to a part of an object. For example style_btn can set a default button appearance, and style_btn_red can overwrite some properties to make the button red-

Every style property you set is specific to a state. For example, you can set a different background color for LV_STATE_DEFAULT and LV_STATE_PRESSED. The library finds the best match between the state of the given part and the available style properties. For example if the object is in pressed state and the border width is specified for pressed state, then it will be used. However, if it's not specified for pressed state, the LV_STATE_DEFAULT's border width will be used. If the border width not defined for LV_STATE_DEFAULT either, a default value will be used.

Some properties (typically the text-related ones) can be inherited. It means if a property is not set in an object it will be searched in its parents too. For example you can set the font once in the screen's style and every text will inherit it by default.

Local style properties also can be added to the objects.


Themes are the default styles of the objects. The styles from the themes are applied automatically when the objects are created.

You can select the theme to use in lv_conf.h.


Button with label

Simple button with label with LVGL
#include "../../lv_examples.h"

static void btn_event_cb(lv_obj_t * btn, lv_event_t event)
    if(event == LV_EVENT_CLICKED) {
        static uint8_t cnt = 0;

        /*Get the first child of the button which is the label and change its text*/
        lv_obj_t * label = lv_obj_get_child(btn, NULL);
        lv_label_set_text_fmt(label, "Button: %d", cnt);

 * Create a button with a label and react on Click event.
void lv_ex_get_started_1(void)
    lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL);     /*Add a button the current screen*/
    lv_obj_set_pos(btn, 10, 10);                            /*Set its position*/
    lv_obj_set_size(btn, 120, 50);                          /*Set its size*/
    lv_obj_set_event_cb(btn, btn_event_cb);                 /*Assign a callback to the button*/

    lv_obj_t * label = lv_label_create(btn, NULL);          /*Add a label to the button*/
    lv_label_set_text(label, "Button");                     /*Set the labels text*/

Styling buttons

Styling buttons with LVGL
#include "../../lv_examples.h"

 * Create styles from scratch for buttons.
void lv_ex_get_started_2(void)
    static lv_style_t style_btn;
    static lv_style_t style_btn_red;

    /*Create a simple button style*/
    lv_style_set_radius(&style_btn, LV_STATE_DEFAULT, 10);
    lv_style_set_bg_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_style_set_bg_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_SILVER);
    lv_style_set_bg_grad_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_GRAY);
    lv_style_set_bg_grad_dir(&style_btn, LV_STATE_DEFAULT, LV_GRAD_DIR_VER);

    /*Swap the colors in pressed state*/
    lv_style_set_bg_color(&style_btn, LV_STATE_PRESSED, LV_COLOR_GRAY);
    lv_style_set_bg_grad_color(&style_btn, LV_STATE_PRESSED, LV_COLOR_SILVER);

    /*Add a border*/
    lv_style_set_border_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);
    lv_style_set_border_opa(&style_btn, LV_STATE_DEFAULT, LV_OPA_70);
    lv_style_set_border_width(&style_btn, LV_STATE_DEFAULT, 2);

    /*Different border color in focused state*/
    lv_style_set_border_color(&style_btn, LV_STATE_FOCUSED, LV_COLOR_BLUE);
    lv_style_set_border_color(&style_btn, LV_STATE_FOCUSED | LV_STATE_PRESSED, LV_COLOR_NAVY);

    /*Set the text style*/
    lv_style_set_text_color(&style_btn, LV_STATE_DEFAULT, LV_COLOR_WHITE);

    /*Make the button smaller when pressed*/
    lv_style_set_transform_height(&style_btn, LV_STATE_PRESSED, -5);
    lv_style_set_transform_width(&style_btn, LV_STATE_PRESSED, -10);
    /*Add a transition to the size change*/
    static lv_anim_path_t path;
    lv_anim_path_set_cb(&path, lv_anim_path_overshoot);

    lv_style_set_transition_prop_1(&style_btn, LV_STATE_DEFAULT, LV_STYLE_TRANSFORM_HEIGHT);
    lv_style_set_transition_prop_2(&style_btn, LV_STATE_DEFAULT, LV_STYLE_TRANSFORM_WIDTH);
    lv_style_set_transition_time(&style_btn, LV_STATE_DEFAULT, 300);
    lv_style_set_transition_path(&style_btn, LV_STATE_DEFAULT, &path);

    /*Create a red style. Change only some colors.*/
    lv_style_set_bg_color(&style_btn_red, LV_STATE_DEFAULT, LV_COLOR_RED);
    lv_style_set_bg_grad_color(&style_btn_red, LV_STATE_DEFAULT, LV_COLOR_MAROON);
    lv_style_set_bg_color(&style_btn_red, LV_STATE_PRESSED, LV_COLOR_MAROON);
    lv_style_set_bg_grad_color(&style_btn_red, LV_STATE_PRESSED, LV_COLOR_RED);
    lv_style_set_text_color(&style_btn_red, LV_STATE_DEFAULT, LV_COLOR_WHITE);
    /*Create buttons and use the new styles*/
    lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL);     /*Add a button the current screen*/
    lv_obj_set_pos(btn, 10, 10);                            /*Set its position*/
    lv_obj_set_size(btn, 120, 50);                          /*Set its size*/
    lv_obj_reset_style_list(btn, LV_BTN_PART_MAIN);         /*Remove the styles coming from the theme*/
    lv_obj_add_style(btn, LV_BTN_PART_MAIN, &style_btn);

    lv_obj_t * label = lv_label_create(btn, NULL);          /*Add a label to the button*/
    lv_label_set_text(label, "Button");                     /*Set the labels text*/

    /*Create a new button*/
    lv_obj_t * btn2 = lv_btn_create(lv_scr_act(), btn);
    lv_obj_set_pos(btn2, 10, 80);
    lv_obj_set_size(btn2, 120, 50);                             /*Set its size*/
    lv_obj_reset_style_list(btn2, LV_BTN_PART_MAIN);         /*Remove the styles coming from the theme*/
    lv_obj_add_style(btn2, LV_BTN_PART_MAIN, &style_btn);
    lv_obj_add_style(btn2, LV_BTN_PART_MAIN, &style_btn_red);   /*Add the red style on top of the current */
    lv_obj_set_style_local_radius(btn2, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_RADIUS_CIRCLE); /*Add a local style*/

    label = lv_label_create(btn2, NULL);          /*Add a label to the button*/
    lv_label_set_text(label, "Button 2");                     /*Set the labels text*/

Slider and alignment

Create a slider with LVGL
#include "../../lv_examples.h"

static lv_obj_t * label;

static void slider_event_cb(lv_obj_t * slider, lv_event_t event)
    if(event == LV_EVENT_VALUE_CHANGED) {
        /*Refresh the text*/
        lv_label_set_text_fmt(label, "%d", lv_slider_get_value(slider));

 * Create a slider and write its value on a label.
void lv_ex_get_started_3(void)
    /* Create a slider in the center of the display */
    lv_obj_t * slider = lv_slider_create(lv_scr_act(), NULL);
    lv_obj_set_width(slider, 200);                        /*Set the width*/
    lv_obj_align(slider, NULL, LV_ALIGN_CENTER, 0, 0);    /*Align to the center of the parent (screen)*/
    lv_obj_set_event_cb(slider, slider_event_cb);         /*Assign an event function*/

    /* Create a label below the slider */
    label = lv_label_create(lv_scr_act(), NULL);
    lv_label_set_text(label, "0");
    lv_obj_set_auto_realign(slider, true);                          /*To keep center alignment when the width of the text changes*/
    lv_obj_align(label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);    /*Align below the slider*/


Learn more about Micropython.

# Create a Button and a Label
scr = lv.obj()
btn = lv.btn(scr)
btn.align(lv.scr_act(), lv.ALIGN.CENTER, 0, 0)
label = lv.label(btn)

# Load the screen