Support native lazy-loading for images rendered by attrs.ImageAttr #409

Closed
opened 2026-04-05 16:30:16 +02:00 by MrUnknownDE · 0 comments
Owner

Originally created by @pheus on 2/6/2026

NetBox version

v4.5.2

Feature type

Change to existing functionality

Proposed functionality

Thanks a lot for all the work that did go into the UI refactor in v4.5.

I noticed that the new attrs.ImageAttr currently renders an <img> tag without the native HTML lazy-loading attribute. For views that display many uploaded images, this means every image is fetched eagerly.

I’d like to propose adding an opt-in keyword argument to attrs.ImageAttr to enable loading="lazy" on the rendered <img> element.

Implementation idea:

class ImageAttr(ObjectAttribute):
    """
    An attribute representing an image field on the model. Displays the uploaded image.
    """
    template_name = 'ui/attrs/image.html'

    def __init__(self, *args, load_lazy=False, **kwargs):
        super().__init__(*args, **kwargs)
        self.load_lazy = load_lazy

    def get_context(self, obj, context):
        return {
            'load_lazy': self.load_lazy,
        }

Template change:

<a href="{{ value.url }}">
  <img
    src="{{ value.url }}"
    alt="{{ value.name }}"
    class="img-fluid"
    {% if load_lazy %}loading="lazy"{% endif %}
  />
</a>

Notes:

  • Default remains unchanged (load_lazy = False) to preserve current behavior.
  • This is purely additive and can be enabled only where it makes sense.

Use case

  • Pages that render many images (e.g., lists/overviews or detail pages with multiple related objects containing images) can become noticeably slower and heavier on bandwidth because images are loaded immediately.
  • Native lazy-loading is broadly supported and provides an easy performance win with minimal complexity.
  • Making it configurable per attribute lets UI code/plugins opt in only when appropriate (e.g., for image-heavy pages), without forcing lazy loading everywhere.

Database changes

None.

External dependencies

None.

*Originally created by @pheus on 2/6/2026* ### NetBox version v4.5.2 ### Feature type Change to existing functionality ### Proposed functionality Thanks a lot for all the work that did go into the UI refactor in v4.5. I noticed that the new `attrs.ImageAttr` currently renders an `<img>` tag without the native HTML lazy-loading attribute. For views that display many uploaded images, this means every image is fetched eagerly. I’d like to propose adding an **opt-in** keyword argument to `attrs.ImageAttr` to enable `loading="lazy"` on the rendered `<img>` element. Implementation idea: ```python class ImageAttr(ObjectAttribute): """ An attribute representing an image field on the model. Displays the uploaded image. """ template_name = 'ui/attrs/image.html' def __init__(self, *args, load_lazy=False, **kwargs): super().__init__(*args, **kwargs) self.load_lazy = load_lazy def get_context(self, obj, context): return { 'load_lazy': self.load_lazy, } ``` Template change: ```django <a href="{{ value.url }}"> <img src="{{ value.url }}" alt="{{ value.name }}" class="img-fluid" {% if load_lazy %}loading="lazy"{% endif %} /> </a> ``` Notes: - Default remains unchanged (`load_lazy = False`) to preserve current behavior. - This is purely additive and can be enabled only where it makes sense. ### Use case - Pages that render many images (e.g., lists/overviews or detail pages with multiple related objects containing images) can become noticeably slower and heavier on bandwidth because images are loaded immediately. - Native lazy-loading is broadly supported and provides an easy performance win with minimal complexity. - Making it **configurable per attribute** lets UI code/plugins opt in only when appropriate (e.g., for image-heavy pages), without forcing lazy loading everywhere. ### Database changes None. ### External dependencies None.
MrUnknownDE added the status: acceptedcomplexity: lowtype: featurestatus: acceptednetboxstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedstatus: acceptedcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lowcomplexity: lownetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxnetboxtype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: featuretype: feature labels 2026-04-05 16:30:24 +02:00
Sign in to join this conversation.
No Label complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low complexity: low netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox netbox status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted status: accepted type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature type: feature
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github/netbox#409