Quick answer: TextureRect defaults to stretch_mode = STRETCH_SCALE, which stretches the image to fill the entire rect without preserving aspect ratio. To fix this, set stretch_mode to STRETCH_KEEP_ASPECT or STRETCH_KEEP_ASPECT_CENTERED to maintain the image's original proportions.
Here is how to fix Godot texturerect image stretched cropped. Your carefully crafted sprite or UI image looks perfect in your art tool, but the moment you drop it into a TextureRect in Godot 4, it is stretched, squished, or cropped in ways that make it unrecognizable. The aspect ratio is wrong, the edges are cut off, or the image is scaled to fill the entire rect ignoring its original proportions. This is a TextureRect configuration issue, and understanding Godot’s stretch and expand modes will fix it immediately.
The Symptom
You assign a texture to a TextureRect node and the image displays distorted. A square icon appears as a wide rectangle. A character portrait is squished vertically. A background image has its sides cropped off. The image’s pixel dimensions do not match what you see on screen, and resizing the TextureRect in the editor makes the distortion worse rather than better.
In some cases the TextureRect appears at the correct size in the editor but stretches incorrectly at runtime when the window is resized or when running on a different aspect ratio. Mobile devices and ultrawide monitors are particularly prone to triggering this behavior.
What Causes This
TextureRect has two key properties that control how images are displayed: stretch_mode and expand_mode. The default stretch_mode is STRETCH_SCALE, which stretches the texture to fill the entire rect without any regard for aspect ratio. If your TextureRect is 200x100 pixels and your image is 100x100 pixels, the image gets stretched to 200x100 — doubling its width while keeping height the same.
The expand_mode property controls whether the TextureRect can be larger than the texture itself. When set to EXPAND_KEEP_SIZE (the default), the TextureRect’s minimum size is locked to the texture’s native pixel dimensions. This means it cannot be made smaller than the texture, which causes layout issues in responsive UI designs. When set to EXPAND_IGNORE_SIZE, the rect sizes itself based on the layout system and ignores the texture dimensions, which is what you usually want for UI images that need to adapt to different screen sizes.
A third contributor is the interaction between TextureRect and its parent container. If the TextureRect is inside a VBoxContainer with SIZE_EXPAND_FILL on both axes, the container forces the rect to a size that may not match the image’s aspect ratio, and the default stretch mode blindly fills the space.
The Fix
Step 1: Choose the right stretch_mode for your use case. Godot 4 offers several options. Here are the most useful ones:
# Fit inside the rect, preserving aspect ratio (letterboxed)
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT
# Fit inside and center (letterboxed, centered)
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
# Fill the rect completely, cropping overflow (no letterboxing)
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
# Stretch to fill without preserving aspect (default, usually wrong)
# texture_rect.stretch_mode = TextureRect.STRETCH_SCALE
Step 2: Set expand_mode for layout flexibility. If your TextureRect lives inside a container and needs to adapt to available space, set expand mode to ignore the texture’s native size:
@onready var portrait = $UI/Portrait
func _ready():
# Let the layout system control sizing
portrait.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
# Keep aspect ratio, fill the space, crop overflow
portrait.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
Step 3: Configure size flags when inside a container. For TextureRect nodes inside VBoxContainer, HBoxContainer, or GridContainer, set the size flags to control how the rect fills available space:
# Inside a VBoxContainer: fill width, use specific height
portrait.size_flags_horizontal = Control.SIZE_EXPAND_FILL
portrait.custom_minimum_size = Vector2(0, 200)
# Or for a grid layout: fill both axes
portrait.size_flags_horizontal = Control.SIZE_EXPAND_FILL
portrait.size_flags_vertical = Control.SIZE_EXPAND_FILL
Step 4: Handle runtime resolution changes. If your game supports window resizing or multiple aspect ratios, ensure the TextureRect updates correctly by connecting to the viewport size changed signal:
func _ready():
get_viewport().connect("size_changed", _on_viewport_resized)
_setup_texture()
func _setup_texture():
portrait.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
portrait.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
func _on_viewport_resized():
# Force layout recalculation if needed
portrait.queue_redraw()
Why This Works
The stretch_mode property directly controls the shader and draw call that renders the texture inside the TextureRect’s bounding rectangle. When set to STRETCH_KEEP_ASPECT, the engine calculates the largest rectangle that fits inside the TextureRect while maintaining the image’s original width-to-height ratio. The remaining space is left empty (letterboxed). When set to STRETCH_KEEP_ASPECT_COVERED, the engine instead calculates the smallest rectangle that completely covers the TextureRect while maintaining aspect ratio, cropping the overflow.
The expand_mode property works at the layout level rather than the rendering level. It determines the minimum size constraint that TextureRect contributes to the parent container’s layout calculations. By setting it to EXPAND_IGNORE_SIZE, you decouple the TextureRect’s layout behavior from the texture’s pixel dimensions. This is essential for responsive UI designs where image containers should adapt to screen size rather than being locked to the source image’s resolution.
Together, these two properties give you precise control: expand_mode determines how big the rect is, and stretch_mode determines how the image fills that rect.
Related Issues
If your images look correct in size but the UI layout around them is broken, check our guide on fixing ScrollContainer scrolling — TextureRect minimum size can interfere with scroll calculations when expand_mode is not configured properly.
For text displayed over images using RichTextLabel with the [img] BBCode tag, see our post on fixing BBCode rendering in RichTextLabel. Inline images follow different sizing rules than standalone TextureRect nodes.
If your TextureRect is capturing clicks that should pass through to the game world behind it, our guide on fixing UI clicks passing through to the game world explains mouse filter settings that apply to all Control nodes including TextureRect.
Stretch mode for rendering. Expand mode for layout. Both matter.