# Attribution > explicit control over the attribution box. --- .. llms_copy::Attribution .. toc:: ### Overview Mirrors dash-leaflet 1.x: pass `attributionControl=False` to the Map to suppress Leaflet 2's built-in attribution control, then add a `dl2.AttributionControl` as a child to position it explicitly and customize the prefix. • `position` ∈ {'topleft','topright','bottomleft','bottomright'} — [MUTABLE] • `prefix` — string of HTML (any anchor / icon / text), or `False` to hide the Leaflet link entirely. [MUTABLE] Bonus: a Switch toggles whether the `dl2.AttributionControl` is mounted at all — which proves the `attributionControl=False` map option does suppress the bundled default (without our component, no box appears). ### Live demo .. exec::docs.attribution.example :code: false ### Source ```python # File: docs/attribution/example.py """ AttributionControl — explicit control over the attribution box. Mirrors dash-leaflet 1.x: pass `attributionControl=False` to the Map to suppress Leaflet 2's built-in attribution control, then add a `dl2.AttributionControl` as a child to position it explicitly and customize the prefix. • `position` ∈ {'topleft','topright','bottomleft','bottomright'} — [MUTABLE] • `prefix` — string of HTML (any anchor / icon / text), or `False` to hide the Leaflet link entirely. [MUTABLE] Bonus: a Switch toggles whether the `dl2.AttributionControl` is mounted at all — which proves the `attributionControl=False` map option does suppress the bundled default (without our component, no box appears). """ import dash_leaflet2 as dl2 import dash_mantine_components as dmc from dash import ( Input, Output, State, callback, clientside_callback, dcc, html, no_update, ) from dash_iconify import DashIconify from dl2_tiles import VOYAGER, register_theme_swap from dl2_locations import BOSTON from dl2_shared import code_panel, header, info_panel # Basemap pair for this page. dl2_tiles owns the light/dark wiring so # every example themes the same way — see register_theme_swap below. TILES = VOYAGER TILE_URL = TILES.url("light") ATTR = TILES.attribution() # Three sample prefixes the user can flip between with a SegmentedControl. The # "Custom" branch reads the live TextInput value instead. PREFIX_PRESETS = { "leaflet": ( 'Leaflet' ), "branded": ( '' "🛰️ dash-leaflet2" ), "none": False, } CODE = """import dash_leaflet2 as dl2 dl2.Map( attributionControl=False, # suppress Leaflet 2's built-in control children=[ dl2.TileLayer(), dl2.AttributionControl( id="attr", position="bottomright", prefix='dash-leaflet2', ), ], ) # Both `position` and `prefix` are MUTABLE — drive them from callbacks. @callback(Output("attr", "prefix"), Input("custom-prefix", "value")) def update_prefix(text): return text or False # False hides the prefix entirely """ component = dmc.Stack( [ header( "AttributionControl", "Explicit control over the attribution box. Set `attributionControl=False` on " "the Map to suppress Leaflet 2's built-in control, then add a " "`dl2.AttributionControl` child to choose its corner and prefix HTML. The " "prefix accepts any HTML (anchors, emoji, inline-styled brand marks) — pass " "`False` to hide it entirely.", badge="dl2.AttributionControl", ), dmc.Grid( [ dmc.GridCol( dmc.Paper( dl2.Map( id="attr-map", center=BOSTON.center, zoom=10, attributionControl=False, style={"height": "60vh"}, children=[ dl2.TileLayer( id="attr-tile", url=TILE_URL, attribution=ATTR ), dl2.Marker( position=BOSTON.center, iconify="mdi:lighthouse-on", iconColor="#e8590c", iconSize=32, ), # The component is rendered into a Dash child by the # "mount" callback below — Python toggles whether it's # in the children list at all. html.Div(id="attr-mount"), ], ), shadow="sm", radius="md", withBorder=True, style={"overflow": "hidden"}, ), span={"base": 12, "md": 8}, ), dmc.GridCol( dmc.Stack( [ info_panel( "Mounted?", dmc.Stack( [ dmc.Switch( id="attr-mounted", label="Render dl2.AttributionControl", description="Off → map's attributionControl=False shows nothing.", checked=True, size="sm", ), ], gap="xs", ), ), info_panel( "Position", dmc.SegmentedControl( id="attr-position", data=[ {"value": "topleft", "label": "TL"}, {"value": "topright", "label": "TR"}, {"value": "bottomleft", "label": "BL"}, {"value": "bottomright", "label": "BR"}, ], value="bottomright", size="xs", fullWidth=True, ), ), info_panel( "Prefix", dmc.Stack( [ dmc.SegmentedControl( id="attr-prefix-preset", data=[ { "value": "leaflet", "label": "Leaflet link", }, { "value": "branded", "label": "Branded", }, {"value": "none", "label": "Hidden"}, {"value": "custom", "label": "Custom"}, ], value="branded", size="xs", fullWidth=True, ), dmc.Textarea( id="attr-prefix-custom", placeholder='HTML allowed, e.g. my site', minRows=2, maxRows=5, autosize=True, size="xs", value='🌐 my site', ), dmc.Text( "Picking 'Custom' wires the textarea live to " "AttributionControl.prefix.", size="xs", c="dimmed", ), ], gap="xs", ), ), info_panel( "Live state", dmc.Stack( [ dmc.Text("position", size="xs", c="dimmed"), dmc.Code( id="attr-state-position", children="bottomright", ), dmc.Text( "prefix HTML (None = hidden)", size="xs", c="dimmed", ), dmc.Code( id="attr-state-prefix", block=True, style={ "fontSize": "11px", "whiteSpace": "pre-wrap", }, ), ], gap=4, ), ), ], gap="md", ), span={"base": 12, "md": 4}, ), ] ), code_panel( "dl2.AttributionControl — explicit positioning + custom prefix", CODE ), # Internal store: holds the resolved prefix value (string or False). dcc.Store(id="attr-resolved-prefix", data=PREFIX_PRESETS["branded"]), ], gap="md", ) # ---- prefix preset/custom -> resolved prefix value ------------------------- @callback( Output("attr-resolved-prefix", "data"), Input("attr-prefix-preset", "value"), Input("attr-prefix-custom", "value"), ) def resolve_prefix(preset, custom): if preset == "custom": # Empty custom -> False (hide); otherwise the raw HTML string. return custom or False return PREFIX_PRESETS.get(preset, PREFIX_PRESETS["leaflet"]) # ---- mount / unmount the AttributionControl child -------------------------- @callback( Output("attr-mount", "children"), Input("attr-mounted", "checked"), Input("attr-position", "value"), Input("attr-resolved-prefix", "data"), ) def render_attribution_control(mounted, position, prefix): if not mounted: return [] return dl2.AttributionControl( id="attr-ctl", position=position or "bottomright", prefix=prefix, ) # ---- live readouts ---------------------------------------------------------- @callback( Output("attr-state-position", "children"), Output("attr-state-prefix", "children"), Input("attr-position", "value"), Input("attr-resolved-prefix", "data"), ) def state_readouts(position, prefix): if prefix is False: return (position or "bottomright"), "False (prefix hidden)" return (position or "bottomright"), prefix or "(empty string — prefix hidden)" # ---- light/dark tile swap (existing pattern) ------------------------------- register_theme_swap("attr-tile", TILES) ``` :defaultExpanded: false :withExpandedButton: true --- *Source: /attribution*