Position and Size¶
Similar to many other parts of LVGL, the concept of setting coordinates is inspired by CSS. LVGL does not implement CSS completely, but a comparable subset is provided (sometimes with minor adjustments).
In short, this means:
If the position of a parent changes, its children will move along with it.
If a child is partially or fully outside its parent, the parts outside will not be visible.
Explicitly set coordinates are stored in styles (position, size, layout, etc.).
Supports
min-width
,max-width
,min-height
, andmax-height
.Supports pixel, percentage, and
content
units.x = 0
,y = 0
means the top-left corner of the parent, plus the parent's left/top padding and border width.width
andheight
represent the full size. The content area is smaller due to padding and border width.A subset of Flexbox and Grid layouts is supported.
Under the hood, position, size, and alignment are all style properties.
For simplicity, there are dedicated API functions to set position and size. However, to keep LVGL's API leaner, less commonly used coordinate-related functions are only available via style properties.
Here’s how to set position and size directly:
lv_obj_set_x(widget, 10);
lv_obj_set_width(widget, 150);
Or via styles:
/* Using a style sheet */
static lv_style_t style_main;
lv_style_init(&style_main);
lv_style_set_width(&style_main, 100);
lv_obj_t * btn = lv_button_create(lv_screen_active());
lv_obj_add_style(btn, &style_main, LV_PART_MAIN);
/* Using a local style to set the width in the PRESSED state */
lv_obj_set_style_width(btn, 150, LV_PART_PRESSED);
Advantages of using styles:
Easily apply width/height/etc. to multiple widgets. E.g., make all sliders 100×10 px.
Centralized control over values (modify in one place).
Values can be selectively overridden. E.g., one style sets a widget to 100×50, another overrides only the width.
Widget position or size can change by state. E.g., 100 px wide in
LV_STATE_DEFAULT
but 120 px inLV_STATE_PRESSED
.Enables style transitions to animate size/position changes between states.
Important Notes¶
This section describes special cases where LVGL's behavior might be unexpected.
Postponed Coordinate Calculation¶
LVGL does not recalculate coordinates immediately after a change. Instead, widgets are marked as "dirty." LVGL recalculates dirty widgets’ positions, sizes, and layouts just before redrawing the screen.
So, if you change a widget’s position or size and then immediately read it, call
lv_obj_update_layout()
first to ensure the values are up to date.
Note: Since layout and size may depend on the parent,
lv_obj_update_layout()
recalculates coordinates for all widgets on
the screen of the given object.
Removing Styles Makes Coordinates Disappear¶
Coordinates are stored as style properties under the hood. So if you use:
lv_obj_set_x(widget, 20);
LVGL saves x = 20
in the widget’s local style.
Normally this is not important for the user, but if you remove a widget’s styles, its coordinates may be removed too:
/* The size of widget1 will reset to default */
lv_obj_set_size(widget1, 200, 100);
lv_obj_remove_style_all(widget1);
/* widget2 will have 200×100 size in the end */
lv_obj_remove_style_all(widget2);
lv_obj_set_size(widget2, 200, 100);
Boxing Model¶
LVGL follows the CSS border-box
model:
https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing
A widget's "box" includes:
- bounding box:
Full width/height of the widget.
- border width:
The width of the border.
- padding:
Space between the border and the content/children.
- margin:
Space outside the widget (used only in some layouts).
- content:
The content area (bounding box minus border and padding).

The border is drawn inside the bounding box, and padding is applied inside the border. The outline (if any) is drawn outside the bounding box.
Use:
lv_obj_set_content_width(widget, 50);
This sets the width of the content, and the actual widget width will include padding and border.
To retrieve size information:
int32_t w = lv_obj_get_width(widget);
int32_t h = lv_obj_get_height(widget);
int32_t content_w = lv_obj_get_content_width(widget);
int32_t content_h = lv_obj_get_content_height(widget);
Static Positioning¶
The simplest way to position a widget is by pixel values:
lv_obj_set_x(widget, 10); /* From the left */
lv_obj_set_y(widget, 20); /* From the top */
lv_obj_set_pos(widget, 10, 20); /* Set both */
These values are relative to the parent's top-left corner, including the parent’s padding and border.
Dynamic Positioning¶
Align to the Parent¶
You can position a widget relative to any side or corner of the parent:
lv_obj_align(widget, LV_ALIGN_TOP_RIGHT, -10, 5);
Or:
lv_obj_set_align(widget, LV_ALIGN_BOTTOM_MID);
Available anchor points:
Use lv_obj_center(widget) as a shortcut to center a widget.
Align to Another Widget¶
Align to a different reference widget:
lv_obj_align_to(label, btn, LV_ALIGN_OUT_TOP_MID, 0, -10);
This places label
10 px above btn
.
Unlike lv_obj_align
, this alignment is not persistent—repositioning btn
won’t
update label
.

Layouts¶
Flex and Grid layouts can automatically position widgets in rows, columns, or 2D grids. They are fully responsive.
Percentage¶
You can use percentages for dynamic positioning:
lv_obj_set_x(widget, lv_pct(50));
100%
means the parent's content area (the size excluding padding and border).
Static Sizing¶
Set fixed size in pixels:
lv_obj_set_size(widget, 200, 100);
Dynamic Sizing¶
Percentage¶
Set width or height as a percentage of the parent’s content area:
lv_obj_set_height(widget, lv_pct(100));
It also works in a responsive way, meaning the Widget's size will be updated automatically as the parent’s size changes.
Content¶
Use LV_SIZE_CONTENT
to size the widget based on its children:
lv_obj_set_size(cont, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_t * label = lv_label_create(cont);
lv_label_set_text(label, "Some text");
Ignored for hidden (LV_OBJ_FLAG_HIDDEN
) or floating (LV_OBJ_FLAG_FLOATING
)
widgets.
Layouts¶
Flex and Grid layouts can also stretch widgets to control both position and size.
Min and Max Size¶
LVGL supports min-width
, max-width
, min-height
, and max-height
.
Use them to set limits when using LV_SIZE_CONTENT
or percentage sizes:
lv_style_set_max_height(&style, 200);
lv_obj_add_style(widget, &style, LV_STATE_DEFAULT);
You can also use percentages:
lv_style_set_max_height(&style, lv_pct(50));
Translate Position¶
To visually move a widget from its current position without changing its base coordinates:
lv_style_set_translate_y(&style_pressed, -20);
Useful for hover or press effects.
Percentage-based translation is relative to the widget’s own size (not the parent).
Coordinate translation applies after layout and affects scrollbars and
LV_SIZE_CONTENT
.
Transform Size¶
You can also visually increase the widget size without affecting layout:
lv_style_set_transform_width(&style_pressed, 10);
lv_style_set_transform_height(&style_pressed, 10);
Unlike translate
, this is a visual-only effect and does not affect layout,
scrollbar behavior, or container resizing.
DPI Independent Unit¶
If just an integer is used as a size or position unit, it means the number of physical pixels on the display. This also means that if the display is changed to a new one with the same size but higher pixel density, everything will appear smaller.
By using lv_dpx(n), the values are scaled based on the DPI of the display to make the result independent of pixel density.
The reference DPI is 160, on which lv_dpx(100)
returns 100
.
If the DPI were only 80, lv_dpx(100)
would return 50
—only 50 pixels.
The default DPI of the displays is set by LV_DPI_DEF
in lv_conf.h
,
but it can be modified with:
lv_display_set_dpi(disp, new_dpi)
lv_dpx
can be used anywhere a pixel value is needed (x
, y
, width
,
height
, radius
, padding
, etc.).
Note that lv_dpx
is evaluated only once, so if the DPI changes afterward, the value
won’t be updated automatically.
With the help of lv_dpx
, only the pixel values can be scaled. Images and fonts
still need to be changed manually to adapt to a new DPI.