Summer 2026 Wildfires in Europe: State of the Season

EFFIS rapid perimeters, as of 3 July 2026

Author

Pierre Beaucoral

Published

July 3, 2026

Every summer now brings the same headlines out of the Mediterranean, and the same question underneath them: is this year actually worse, or does it just feel that way? The 2026 fire season is about four weeks old, and this page is my attempt at an answer: what has burned so far, where, and how that compares with recent seasons at the same point in the calendar. It is a state of the season, not a verdict.

A word on the data first. Satellites spot large fires, and Europe’s EFFIS service (part of the Copernicus programme) draws the outline of each burned area it can map. I work with those outlines. I measure each fire’s size directly from its shape, using a map projection that keeps areas honest, so hectares are comparable across countries. And I count only the parts that fall inside Europe. The method is the same as in the 2025 season analysis this project grew out of; that post remains the reference for the details.

NoteThree things to know about these fire outlines
  • Only fires of roughly 30 to 50 hectares and up get mapped. Smaller fires are mostly invisible here, so totals sit below official statistics.
  • The outlines are rapid estimates. They are revised as better satellite imagery arrives. Every number on this page will move a little with each weekly update, and that is by design.
  • I compare burned area, not fire counts. The detection system changed in 2023 to 2024 and suddenly saw many more small fires. Counting fires across years is therefore misleading; measuring area is much safer.
Show code
# -------------------- Packages --------------------
library(sf)
library(dplyr)
library(tidyr)
library(tibble)
library(purrr)
library(lubridate)
library(forcats)
library(ggplot2)
library(scales)
library(patchwork)
library(janitor)
library(rnaturalearth)
library(leaflet)
library(htmlwidgets)

# GDAL caps single GeoJSON features at ~200 MB; the 2020 snapshot has one
# giant multipolygon above that (documented in scripts/fetch_effis.R).
Sys.setenv(OGR_GEOJSON_MAX_OBJ_SIZE = "0")

# -------------------- Shared helper library --------------------
source(file.path("R", "helpers.R"))   # to_num, parse_date_any, lab_si_ha
source(file.path("R", "geo.R"))       # Europe polygons, read_effis, tagging
source(file.path("R", "cache.R"))     # cached()
source(file.path("R", "theme.R"))     # pal_lc, theme_burns()
source(file.path("R", "flags.R"))     # flag_table()
source(file.path("R", "pipeline.R"))  # season assembly (all cached)
source(file.path("R", "plots.R"))     # plot_envelope() (shared with index)
source(file.path("scripts", "latest_snapshot.R"))

# -------------------- Season parameters --------------------
YEAR_CURRENT <- 2026L
HIST_YEARS   <- 2017:2025   # archive effectively starts 2016; 2016 kept out of
                            # the band (partial-quality first year), see caveats
SEASON_START_MONTH <- 6L    # season window: 1 Jun -
SEASON_END_MONTH   <- 9L    # 30 Sep

# Calendar heatmap DOES include 2016 (the archive's first, partial-quality
# year) -- unlike HIST_YEARS above, since a full-decade grid is the point of
# that chart; 2016's row is captioned as lower-confidence rather than dropped.
CALENDAR_YEARS <- 2016:2025

snap <- latest_snapshot()
stopifnot(!is.na(snap))
eu <- get_eu()

# -------------------- Core objects (all disk-cached) --------------------
envelope <- build_envelope(
  hist_years = HIST_YEARS, year_current = YEAR_CURRENT,
  snapshot_dir = snap, eu = eu,
  start_month = SEASON_START_MONTH, end_month = SEASON_END_MONTH
)

tagged_2026_full <- get_tagged_full_year(YEAR_CURRENT, snap, eu)

as_of     <- envelope$meta$as_of_date
as_of_lab <- paste(day(as_of), month.name[month(as_of)])   # English, locale-proof

# Season-window (Jun 1 - as-of) headline numbers
season_ha    <- envelope$meta$current_cum_ha
season_n     <- envelope$meta$current_n_fires
pct_vs_med   <- envelope$meta$pct_vs_median
median_ha    <- envelope$meta$median_to_date_ha

# Full-2026-to-date numbers (Jan 1 - as-of), used by maps and rankings
full_ha <- sum(tagged_2026_full$area_ha, na.rm = TRUE)
full_n  <- nrow(tagged_2026_full)

# Land-cover machinery shared by several chunks
lc_cols <- c("broadlea", "conifer", "mixed", "scleroph", "transit",
             "agriareas", "artifsurf", "othernatlc", "otherlc")
lc_labels <- c(
  broadlea = "Broad-leaved forest", conifer = "Coniferous forest",
  mixed = "Mixed forest", scleroph = "Sclerophyllous veg.",
  transit = "Transitional woodland-shrub", agriareas = "Agricultural areas",
  artifsurf = "Artificial surfaces", othernatlc = "Other natural LC",
  otherlc = "Other LC"
)

# -------------------- Figure captions (referenced via !expr) --------------------
cap_envelope <- sprintf(
  paste(
    "Cumulative burned area since 1 June: 2026 to date against the 2017–2025 range.",
    "The shaded band spans the minimum and maximum cumulative trajectory of the nine",
    "previous seasons on each day of the season; the dashed line is their median.",
    "The 2026 line stops at its last mapped perimeter (%s)."
  ),
  as_of_lab
)
cap_hero <- sprintf(
  paste(
    "Burn-scar perimeters mapped by EFFIS in 2026 to date (1 January – %s),",
    "Europe-clipped. Perimeter outlines drawn on national borders; rapid mapping",
    "covers fires of roughly 30–50 ha and above. Source: EFFIS rapid perimeters,",
    "snapshot %s; geometry in EPSG:3035."
  ),
  as_of_lab, basename(snap)
)
cap_facet <- sprintf(
  paste(
    "The same 2026 perimeters split by period: late winter and spring (January–May)",
    "versus the summer window so far (1 June – %s). Panel labels report mapped",
    "perimeters and Europe-clipped burned area per period. Source: EFFIS rapid",
    "perimeters, snapshot %s."
  ),
  as_of_lab, basename(snap)
)
cap_countries <- sprintf(
  paste(
    "Top-10 countries by Europe-clipped burned area, 1 January – %s 2026.",
    "Left: total mapped burned area. Right: the same total as a share of national",
    "land area (land area computed from the same reference polygons,",
    "mainland-Europe crop). Source: EFFIS rapid perimeters, snapshot %s; areas from",
    "geometry in EPSG:3035; countries tagged by maximum overlap."
  ),
  as_of_lab, basename(snap)
)
cap_reburn <- sprintf(
  paste(
    "2026 perimeters (1 January – %s) split into first-time burns (orange-red) and",
    "re-burns of ground already inside a 2017–2025 EFFIS perimeter (blue). Overlap",
    "computed on ~100 m-simplified geometry in EPSG:3035. Source: EFFIS rapid",
    "perimeters, snapshot %s."
  ),
  as_of_lab, basename(snap)
)
cap_landcover <- sprintf(
  paste(
    "Land-cover composition of burned area in the 1 June – %s window: 2026 versus",
    "the pooled (area-weighted) 2017–2025 average for the same window. Shares are",
    "each perimeter's land-cover percentages weighted by its Europe-clipped area.",
    "Source: EFFIS rapid perimeters, snapshot %s."
  ),
  as_of_lab, basename(snap)
)
cap_calendar <- sprintf(
  paste(
    "Weekly Europe-clipped burned area by year, %d–%d, in ISO calendar weeks.",
    "Fill on a square-root scale: many weeks burn exactly zero hectares, which a",
    "log scale cannot display, and sqrt still compresses the July–August peaks",
    "enough to keep smaller months visible. Grey cells are 2026 weeks that have",
    "not happened yet; the dashed line marks the last mapped week so far (%s).",
    "2016 is the archive's first year and its coverage is less complete than",
    "later years. This chart uses burned area, not fire counts, so the",
    "2023-to-2024 detection-system change (which makes counts incomparable",
    "across years) mostly washes out. Source: EFFIS rapid perimeters,",
    "snapshots covering %d–%d; areas from geometry in EPSG:3035, Europe-clipped."
  ),
  min(CALENDAR_YEARS), YEAR_CURRENT, as_of_lab, min(CALENDAR_YEARS), YEAR_CURRENT
)
cap_natura_trend <- sprintf(
  paste(
    "Share of Europe-clipped burned area falling inside a Natura 2000 protected",
    "site, 1 June – %s each year, computed as area-weighted PERCNA2K (EFFIS's own",
    "field for the share of each perimeter's own area inside Natura 2000). The",
    "window is identical every year, so the series is not distorted by how much",
    "of the season had elapsed. 2026 is highlighted. Source: EFFIS rapid",
    "perimeters, snapshots covering 2017–%d."
  ),
  as_of_lab, YEAR_CURRENT
)
cap_natura_map <- sprintf(
  paste(
    "2026 perimeters (1 January – %s) colored by PERCNA2K, the share of each",
    "fire's own perimeter that falls inside a Natura 2000 protected site. Grey:",
    "0%% (no protected-area overlap). Bright yellow: perimeters that burned",
    "almost entirely inside protected land. Source: EFFIS rapid perimeters,",
    "snapshot %s."
  ),
  as_of_lab, basename(snap)
)

Two numbers set the scene, and they cover different time windows. Since 1 June, the start of the summer season, satellites have mapped about 59,000 hectares of burned land in Europe, across 545 separate fire outlines. That is roughly 6 times the area of the city of Paris, and about 250% of what a typical recent season had burned by this date (24,000 hectares). But 2026 did not start in June. Counting from 1 January, the total is already about 240,000 hectares, and an unusually busy late winter and spring drove most of that gap, weeks before most people start watching for fire season at all. That timing is this year’s strangest thread so far, and the calendar chart further down traces exactly when it happened. The chart right below sticks to the summer window, so that seasons are compared like for like; the maps after that show everything mapped in 2026 so far.

Is this season bad so far?

Show code
plot_envelope(envelope)

Cumulative burned area since 1 June: 2026 to date against the 2017–2025 range. The shaded band spans the minimum and maximum cumulative trajectory of the nine previous seasons on each day of the season; the dashed line is their median. The 2026 line stops at its last mapped perimeter (5 July).

Cumulative burned area since 1 June: 2026 to date against the 2017–2025 range. The shaded band spans the minimum and maximum cumulative trajectory of the nine previous seasons on each day of the season; the dashed line is their median. The 2026 line stops at its last mapped perimeter (5 July).

Read this like a race against the past. The grey band is the space between the calmest and the most severe of the last nine seasons, day by day. The dashed line is the middle of the pack. The orange line is 2026, and it stops where the data stops, on 5 July. One month in, 2026 is running above the median (about 2.5 times it) but well inside the band: several recent years, like 2017, 2022 and 2025, were further along at this date. My honest reading is elevated, not exceptional, so far. Most of a European fire season happens in July and August. This line has most of its story still ahead of it, and I will not call a season in week four.

Does Europe only burn in summer?

Not entirely, and 2026 makes the exception hard to miss. The chart below stacks eleven years, calendar week by calendar week, so a season’s full rhythm is visible at once.

Show code
weekly_area <- build_weekly_area(
  years = CALENDAR_YEARS, current_year = YEAR_CURRENT,
  current_tagged = tagged_2026_full, snapshot_dir = snap, eu = eu, lc_cols = lc_cols
)

cal_grid <- prepare_calendar_grid(weekly_area, CALENDAR_YEARS, YEAR_CURRENT, as_of)
cal_cutoff_week <- min(lubridate::isoweek(as_of), 52L)

plot_calendar_heatmap(cal_grid, YEAR_CURRENT, cal_cutoff_week)

Weekly Europe-clipped burned area by year, 2016–2026, in ISO calendar weeks. Fill on a square-root scale: many weeks burn exactly zero hectares, which a log scale cannot display, and sqrt still compresses the July–August peaks enough to keep smaller months visible. Grey cells are 2026 weeks that have not happened yet; the dashed line marks the last mapped week so far (5 July). 2016 is the archive’s first year and its coverage is less complete than later years. This chart uses burned area, not fire counts, so the 2023-to-2024 detection-system change (which makes counts incomparable across years) mostly washes out. Source: EFFIS rapid perimeters, snapshots covering 2016–2026; areas from geometry in EPSG:3035, Europe-clipped.

Weekly Europe-clipped burned area by year, 2016–2026, in ISO calendar weeks. Fill on a square-root scale: many weeks burn exactly zero hectares, which a log scale cannot display, and sqrt still compresses the July–August peaks enough to keep smaller months visible. Grey cells are 2026 weeks that have not happened yet; the dashed line marks the last mapped week so far (5 July). 2016 is the archive’s first year and its coverage is less complete than later years. This chart uses burned area, not fire counts, so the 2023-to-2024 detection-system change (which makes counts incomparable across years) mostly washes out. Source: EFFIS rapid perimeters, snapshots covering 2016–2026; areas from geometry in EPSG:3035, Europe-clipped.
Show code
peak_2026 <- cal_grid |>
  dplyr::filter(year == YEAR_CURRENT, !is.na(area_ha)) |>
  dplyr::slice_max(area_ha, n = 1, with_ties = FALSE)
peak_2026_date <- iso_week_start(YEAR_CURRENT, peak_2026$iso_week)
peak_2026_lab  <- paste(day(peak_2026_date), month.abb[month(peak_2026_date)])

Two patterns jump out. First, most years share the same silhouette: a dark block across July and August and comparatively little else. Second, 2026’s own row breaks that pattern before summer even starts, with a bright stretch of late-winter and early-spring weeks that most other years do not show at this intensity. That is the unusually busy January-to-May window from the introduction, now visible on its own. The single busiest week of 2026 so far was the week of 23 Feb, when about 46 kha burned in that single week alone. The grey band on the right marks weeks that have not happened yet: the row simply stops there, rather than being padded with zeros.

NoteReading this chart honestly
  • The fill is burned area, not fire counts. The detection system changed in 2023 to 2024, which would make later years look artificially busier if counted by number of fires; area is far less affected.
  • 2016 is the archive’s first year. Its coverage, especially outside summer, is less complete than later years, so read that row with extra caution.
  • The color scale is a square root, not a log. Many weeks genuinely burn zero hectares, and a log scale cannot show a true zero.

Where did it burn?

With the timing question answered, the next one is geographic: where, across the continent, did all of this actually happen?

Show code
p_base <- ggplot() +
  geom_sf(data = eu$poly, fill = "grey95", color = "grey70", linewidth = 0.15) +
  coord_sf()

p_base +
  geom_sf(
    data = tagged_2026_full,
    fill = NA, color = "#D64A05", linewidth = 0.25, alpha = 0.8
  ) +
  theme_burns(base_size = 11, map = TRUE)

Burn-scar perimeters mapped by EFFIS in 2026 to date (1 January – 5 July), Europe-clipped. Perimeter outlines drawn on national borders; rapid mapping covers fires of roughly 30–50 ha and above. Source: EFFIS rapid perimeters, snapshot 2026-07-06; geometry in EPSG:3035.

Burn-scar perimeters mapped by EFFIS in 2026 to date (1 January – 5 July), Europe-clipped. Perimeter outlines drawn on national borders; rapid mapping covers fires of roughly 30–50 ha and above. Source: EFFIS rapid perimeters, snapshot 2026-07-06; geometry in EPSG:3035.
Show code
tagged_2026_full <- tagged_2026_full |>
  mutate(period = factor(
    if_else(ba_date < as.Date(sprintf("%d-06-01", YEAR_CURRENT)),
            "January–May", paste("1 June –", as_of_lab)),
    levels = c("January–May", paste("1 June –", as_of_lab))
  ))

period_stats <- tagged_2026_full |>
  st_drop_geometry() |>
  group_by(period) |>
  summarise(n_fires = n(), burned_ha = sum(area_ha, na.rm = TRUE), .groups = "drop") |>
  mutate(lab = paste0("Fires: ", comma(n_fires), "\nArea: ", lab_si_ha(burned_ha)))

bb <- st_bbox(eu$poly)
period_stats <- period_stats |>
  mutate(
    x = bb["xmin"] + 0.03 * (bb["xmax"] - bb["xmin"]),
    y = bb["ymax"] - 0.03 * (bb["ymax"] - bb["ymin"])
  )

p_base +
  geom_sf(
    data = tagged_2026_full,
    fill = "#D64A05", alpha = 0.4, color = "#D64A05", linewidth = 0.08
  ) +
  facet_wrap(~ period, ncol = 2) +
  geom_label(
    data = period_stats, aes(x = x, y = y, label = lab), inherit.aes = FALSE,
    size = 3.1, label.size = 0, hjust = 0, vjust = 1,
    fill = alpha("white", 0.85), colour = "grey20"
  ) +
  theme_burns(base_size = 11, map = TRUE) +
  theme(strip.text = element_text(face = "bold"))

The same 2026 perimeters split by period: late winter and spring (January–May) versus the summer window so far (1 June – 5 July). Panel labels report mapped perimeters and Europe-clipped burned area per period. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

The same 2026 perimeters split by period: late winter and spring (January–May) versus the summer window so far (1 June – 5 July). Panel labels report mapped perimeters and Europe-clipped burned area per period. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

The map makes two things obvious: first, the familiar geography is already in place: the Iberian Peninsula and the Mediterranean arc dominate the map. Second, here is that winter surge again, now on the map: about 181,000 of the 240,000 hectares were mapped between January and May, visible in the left panel above. Keep that in mind when reading the summer chart at the top: it deliberately leaves those months out.

Explore the fires yourself

Show code
# Computed here, ahead of the prose below, so the sliver-drop share it quotes
# is a real inline value rather than an asserted "about 1%". The cache key
# does not need the snapshot identifier: it already includes asof<date>, and
# tagged_2026_full (its only input) is itself built from a snapshot-aware key
# upstream in get_tagged_window().
leaflet_result <- cached(sprintf("leaflet_2026_asof%s", format(as_of, "%Y%m%d")), {
  x <- tagged_2026_full |>
    mutate(across(all_of(lc_cols), to_num))

  lc_mat <- as.matrix(st_drop_geometry(x)[, lc_cols])
  lc_mat[is.na(lc_mat)] <- 0
  dom <- lc_labels[lc_cols[max.col(lc_mat, ties.method = "first")]]
  dom[rowSums(lc_mat) <= 0] <- "n/a"
  x$dominant_lc <- unname(dom)

  total_ha_before <- sum(x$area_ha, na.rm = TRUE)

  x <- x |>
    st_simplify(dTolerance = 100) |>       # ~100 m in EPSG:3035 (metric)
    filter(!st_is_empty(geometry))

  # leaflet only draws polygonal geometry: split GEOMETRYCOLLECTION rows
  # (simplify/clip byproducts) into their polygon parts, drop line slivers,
  # and cast everything to MULTIPOLYGON.
  types <- st_geometry_type(x)
  x_poly <- x[types %in% c("POLYGON", "MULTIPOLYGON"), ]
  x_gc   <- x[types == "GEOMETRYCOLLECTION", ]
  if (nrow(x_gc) > 0L) {
    x_poly <- rbind(x_poly, suppressWarnings(st_collection_extract(x_gc, "POLYGON")))
  }
  x_poly <- x_poly |>
    filter(!st_is_empty(geometry)) |>
    st_cast("MULTIPOLYGON") |>
    st_transform(4326)

  # Sliver-drop share quoted in prose: the area held by rows that degenerated
  # into pure lines/points (or emptied out entirely) after simplification,
  # and so never make it into x_poly, as a share of the pre-simplification
  # total -- NOT the much smaller boundary shrinkage of polygons that ARE
  # retained.
  retained_ha <- sum(x_poly$area_ha, na.rm = TRUE)
  sliver_pct <- 100 * (total_ha_before - retained_ha) / total_ha_before

  list(sf = x_poly, sliver_pct = sliver_pct)
}, version = 3)

leaf_sf <- leaflet_result$sf
sliver_pct_lab <- sprintf("%.1f%%", leaflet_result$sliver_pct)

Every 2026 fire, on a map you can pan and zoom. Click an outline for its date, size, country, and the vegetation that dominated the ground it burned. I simplified the shapes slightly (about 100 m of tolerance) to keep the page fast; the tiniest border slivers, about 1.1% of the total area, drop out in the process. For survey-grade outlines, use the official EFFIS viewer.

Show code
pal_period <- c("#3E8EC4", "#D64A05")  # winter-spring blue vs summer orange-red
leaflet(leaf_sf) |>
  addProviderTiles("CartoDB.Positron") |>
  addPolygons(
    weight = 0.7,
    color = ~if_else(period == "January–May", pal_period[1], pal_period[2]),
    fillColor = ~if_else(period == "January–May", pal_period[1], pal_period[2]),
    fillOpacity = 0.35,
    popup = ~sprintf(
      "<b>%s</b><br/>Date: %s<br/>Mapped area: %s ha<br/>Dominant land cover: %s",
      name_long, format(ba_date, "%Y-%m-%d"), comma(round(area_ha)), dominant_lc
    )
  ) |>
  addLegend(
    position = "bottomleft", colors = pal_period,
    labels = c("January–May", paste("1 June –", as_of_lab)),
    title = "2026 perimeters", opacity = 0.7
  )

What does a giant fire actually look like?

Scrolling through that map, numbers like “seven thousand hectares” are hard to picture on their own. Below are the ten largest 2026 fires so far, each drawn at the exact same scale, so size differences are visible rather than just stated. A single grey circle, equal in area to the city of Paris, sits alongside them for reference.

Show code
# Built here (not in the shared setup chunk) because it only depends on
# tagged_2026_full, which the setup chunk already produced; the caption
# below needs gallery$half_side, so it is composed in this data chunk and
# only REFERENCED (via !expr) by the figure chunk that follows -- fig-cap
# options resolve before a chunk's own body runs, so a caption cannot depend
# on a value the same chunk is about to compute.
gallery <- build_gallery_scars(
  tagged_2026_full, n = 10L, lc_cols = lc_cols, lc_labels = lc_labels
)

cap_gallery <- sprintf(
  paste(
    "The ten largest 2026 fires by Europe-clipped area, each drawn at the same",
    "metric scale (a single shared window, about %.0f km across) so their true",
    "relative size is directly comparable; fill shows each fire's dominant",
    "land-cover class. The grey circle is not a fire: it is a circle of equal",
    "area to Paris intra-muros (about 105 km2, 10,500 ha), drawn for scale",
    "rather than traced from an official city boundary. Communes and country",
    "codes from EFFIS attributes; rapid perimeters are provisional and",
    "subject to revision. Source: EFFIS rapid perimeters, snapshot %s."
  ),
  gallery$half_side * 2 / 1000, basename(snap)
)
Show code
plot_gallery_scars(gallery)

The ten largest 2026 fires by Europe-clipped area, each drawn at the same metric scale (a single shared window, about 30 km across) so their true relative size is directly comparable; fill shows each fire’s dominant land-cover class. The grey circle is not a fire: it is a circle of equal area to Paris intra-muros (about 105 km2, 10,500 ha), drawn for scale rather than traced from an official city boundary. Communes and country codes from EFFIS attributes; rapid perimeters are provisional and subject to revision. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

The ten largest 2026 fires by Europe-clipped area, each drawn at the same metric scale (a single shared window, about 30 km across) so their true relative size is directly comparable; fill shows each fire’s dominant land-cover class. The grey circle is not a fire: it is a circle of equal area to Paris intra-muros (about 105 km2, 10,500 ha), drawn for scale rather than traced from an official city boundary. Communes and country codes from EFFIS attributes; rapid perimeters are provisional and subject to revision. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

Fires like these are not typical of the season. Across all 7,163 mapped 2026 perimeters, the largest 1% alone hold about 36% of the season’s total burned area. Most fires are small. A handful of monsters like the ones above do most of the damage, and the biggest one on the sheet, in Cambra e Carvalhal de Vermilhas, Portugal, is already bigger than the Paris-sized circle next to it.

See it for yourself: drop each fire onto Paris

A grey circle is one way to picture “the size of Paris,” but the real city is not a circle. What if you could see a fire’s true, ragged outline sitting directly on the streets you might actually know? The map below does exactly that: it takes the true shape of each of the 10 fires above and slides it onto the real municipal boundary of Paris, one fire at a time, so the comparison is against the actual city rather than a stand-in shape.

Show code
# Reuses gallery$panels/gallery$meta (already computed for the static
# figure above) rather than re-selecting or re-simplifying the top-10 from
# scratch; build_paris_comparison() only adds a translation ("+ Paris
# centroid") on top of geometry build_gallery_scars() already produced.
# Cache key mirrors the leaflet-data chunk's convention above: asof<date>
# is enough because gallery (its only input besides the static Paris
# boundary asset) is itself built from snapshot-aware tagged_2026_full.
paris_cmp <- cached(
  sprintf("paris_comparison_asof%s", format(as_of, "%Y%m%d")),
  build_paris_comparison(gallery),
  version = 1
)

paris_fires <- paris_cmp$fires_wgs84 |>
  mutate(
    fill_col = unname(pal_lc[dominant_lc]),
    times_paris = area_ha / paris_cmp$paris_ha,
    group_label = sprintf(
      "%s. %s (%s), %s ha", rank, commune, iso_a2, comma(round(area_ha))
    ),
    popup_html = sprintf(
      paste(
        "<b>%02d. %s</b><br/>Country: %s<br/>Date: %s<br/>",
        "Mapped area: %s ha<br/><b>%.1f×</b> the area of Paris (%s ha)"
      ),
      as.integer(rank), commune, name_long, format(ba_date, "%Y-%m-%d"),
      comma(round(area_ha)), times_paris, comma(round(paris_cmp$paris_ha))
    )
  )
NoteRead this map honestly
  • These fires never happened anywhere near Paris. Their real locations are on the maps earlier in this post; here, each outline is moved so its size can be compared directly to a familiar shape.
  • Paris’s own outline is its real municipal boundary (INSEE code 75056, not a circle this time), about 10,535 hectares by this geometry (close to, but not identical to, the round 10,500 ha used as a reference constant elsewhere on this page). Boundary data: france-geojson (Etalab, Licence Ouverte).
  • Pick a fire from the list on the right; only one outline is shown at a time, so sizes are never visually stacked on top of each other.
Show code
# One dark, permanent outline (never toggles) for Paris itself, plus one
# leaflet GROUP per fire; addLayersControl(baseGroups = ...) makes group
# selection mutually exclusive (radio-style), and hideGroup() on every
# label but the first leaves fire 01 selected by default.
combined_geom <- c(sf::st_geometry(paris_cmp$paris_wgs84), sf::st_geometry(paris_fires))
bbox_all <- sf::st_bbox(combined_geom)

paris_map <- leaflet(width = "100%", height = 520) |>
  addProviderTiles("CartoDB.Positron") |>
  addPolygons(
    data = paris_cmp$paris_wgs84,
    color = "#1A1A1A", weight = 2.4, fill = FALSE, opacity = 0.9
  ) |>
  fitBounds(
    lng1 = unname(bbox_all["xmin"]), lat1 = unname(bbox_all["ymin"]),
    lng2 = unname(bbox_all["xmax"]), lat2 = unname(bbox_all["ymax"])
  )

paris_map <- purrr::reduce(seq_len(nrow(paris_fires)), function(map_acc, i) {
  row_i <- paris_fires[i, ]
  addPolygons(
    map_acc,
    data = row_i, group = row_i$group_label,
    color = "grey20", weight = 1,
    fillColor = row_i$fill_col, fillOpacity = 0.55,
    popup = row_i$popup_html
  )
}, .init = paris_map)

paris_map <- paris_map |>
  addLayersControl(
    baseGroups = paris_fires$group_label,
    options = layersControlOptions(collapsed = FALSE),
    position = "topright"
  )

# Default selection: fire 01 (already the largest, per gallery's arrange()).
paris_map <- purrr::reduce(
  paris_fires$group_label[-1],
  function(map_acc, g) hideGroup(map_acc, g),
  .init = paris_map
)

paris_map

Which countries are hit hardest so far?

Individual fires are dramatic on their own; country totals show where the season is landing hardest overall.

Show code
ctry_2026 <- tagged_2026_full |>
  st_drop_geometry() |>
  group_by(name_long) |>
  summarise(burned_ha = sum(area_ha, na.rm = TRUE), n_fires = n(), .groups = "drop") |>
  left_join(country_land_area(eu), by = "name_long") |>
  mutate(pct_land = 100 * burned_ha / land_area_ha)

top10 <- ctry_2026 |>
  slice_max(burned_ha, n = 10)

flags <- flag_table(top10$name_long, eu$poly)
top10 <- top10 |>
  left_join(flags, by = "name_long") |>
  # factor AFTER the join: joining a factor to a character column silently
  # coerces name_long back to character, which would both scramble the bar
  # order (alphabetical) and break the numeric y positions for the flags
  mutate(name_long = fct_reorder(name_long, burned_ha))

max_x <- max(top10$burned_ha)

# Flag PNGs drawn with base annotation_raster() (no extra image package
# needed): one raster layer per country, placed just right of its bar.
# Discrete y positions are the factor level codes (1..10).
flag_df <- top10 |>
  filter(!is.na(flag_path)) |>
  mutate(y_pos = as.integer(name_long))
flag_layers <- lapply(seq_len(nrow(flag_df)), function(i) {
  x0 <- flag_df$burned_ha[i] + 0.05 * max_x
  annotation_raster(
    png::readPNG(flag_df$flag_path[i]),
    xmin = x0, xmax = x0 + 0.08 * max_x,
    ymin = flag_df$y_pos[i] - 0.28, ymax = flag_df$y_pos[i] + 0.28
  )
})

p_raw <- ggplot(top10, aes(x = burned_ha, y = name_long)) +
  geom_col(fill = "grey90") +
  geom_point(aes(color = col), size = 3) +
  scale_color_identity() +
  flag_layers +
  scale_x_continuous(labels = lab_si_ha, expand = expansion(mult = c(0, 0.18))) +
  labs(x = "Burned area (ha)", y = NULL) +
  theme_burns(base_size = 12) +
  coord_cartesian(clip = "off")

p_pct <- ggplot(top10, aes(x = pct_land, y = name_long)) +
  geom_segment(aes(x = 0, xend = pct_land, yend = name_long, color = col),
               linewidth = 1.1, show.legend = FALSE) +
  geom_point(aes(color = col), size = 3.2) +
  scale_color_identity() +
  scale_x_continuous(labels = label_percent(scale = 1),
                     expand = expansion(mult = c(0, 0.08))) +
  labs(x = "Share of national land area", y = NULL) +
  theme_burns(base_size = 12) +
  theme(axis.text.y = element_blank())

p_raw + p_pct +
  plot_layout(widths = c(3, 2))

Top-10 countries by Europe-clipped burned area, 1 January – 5 July 2026. Left: total mapped burned area. Right: the same total as a share of national land area (land area computed from the same reference polygons, mainland-Europe crop). Source: EFFIS rapid perimeters, snapshot 2026-07-06; areas from geometry in EPSG:3035; countries tagged by maximum overlap.

Top-10 countries by Europe-clipped burned area, 1 January – 5 July 2026. Left: total mapped burned area. Right: the same total as a share of national land area (land area computed from the same reference polygons, mainland-Europe crop). Source: EFFIS rapid perimeters, snapshot 2026-07-06; areas from geometry in EPSG:3035; countries tagged by maximum overlap.

Spain leads the raw ranking with about 63,000 hectares so far. But raw hectares flatter big countries. The right panel asks a fairer question: how much of the country’s own land has burned? Small countries can be hit much harder relative to their size, and that is where the order reshuffles.

Is fire returning to old scars?

Some land burns again and again. To see how much of 2026 falls on ground that already burned recently, I merged all the fire outlines from 2017 to 2025 into one big historical footprint, then checked how much of this year’s burned area lands inside it.

Show code
footprint <- get_historical_footprint(HIST_YEARS, snap, eu, tol_m = 100)

reburn <- cached(
  sprintf("reburn_2026_asof%s_tol100", format(as_of, "%Y%m%d")),
  compute_reburn(tagged_2026_full, footprint),
  version = 1
)

reburn_layers <- rbind(
  st_sf(kind = "First-time burn (not in 2017–2025 scars)", geometry = reburn$new_geom),
  st_sf(kind = "Re-burn of a 2017–2025 scar", geometry = reburn$reburn_geom)
)

ggplot() +
  geom_sf(data = eu$poly, fill = "grey95", color = "grey70", linewidth = 0.15) +
  geom_sf(data = reburn_layers, aes(fill = kind, color = kind),
          linewidth = 0.1, alpha = 0.65) +
  scale_fill_manual(values = c(
    "First-time burn (not in 2017–2025 scars)" = "#D64A05",
    "Re-burn of a 2017–2025 scar" = "#1F78B4"
  ), name = NULL) +
  scale_color_manual(values = c(
    "First-time burn (not in 2017–2025 scars)" = "#D64A05",
    "Re-burn of a 2017–2025 scar" = "#1F78B4"
  ), name = NULL) +
  theme_burns(base_size = 11, map = TRUE) +
  theme(legend.position = "bottom")

2026 perimeters (1 January – 5 July) split into first-time burns (orange-red) and re-burns of ground already inside a 2017–2025 EFFIS perimeter (blue). Overlap computed on ~100 m-simplified geometry in EPSG:3035. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

2026 perimeters (1 January – 5 July) split into first-time burns (orange-red) and re-burns of ground already inside a 2017–2025 EFFIS perimeter (blue). Overlap computed on ~100 m-simplified geometry in EPSG:3035. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

On the map, France’s scars lean toward re-burns of familiar ground, while Spain and Portugal’s fires more often break new ground.

Show code
aude_rows <- tagged_2026_full |>
  st_drop_geometry() |>
  filter(!is.na(province), grepl("Aude", province, fixed = TRUE))
aude_n  <- nrow(aude_rows)
aude_ha <- sum(aude_rows$area_ha, na.rm = TRUE)

fr_rows <- tagged_2026_full |> st_drop_geometry() |> filter(name_long == "France")
fr_n  <- nrow(fr_rows)
fr_ha <- sum(fr_rows$area_ha, na.rm = TRUE)

The headline: about 41% of 2026’s burned area (roughly 99,000 of 240,000 hectares) sits on land that already burned at least once since 2017. That is not automatically bad news. Mediterranean shrubland and many pine forests are fire-adapted: they regrow quickly after a burn, and that fresh growth is often exactly the kind of fine, dry fuel that carries a fire well again a few years later. Repeated burning on a short cycle has long been part of how these landscapes work. What the share does not tell us is whether any single re-burn was more or less severe than the fire before it, or whether the gap between fires is shrinking: it is a description of overlap, not a verdict on land condition. Treat the exact figure as an estimate too. Both layers are rapid mapping products, and I simplified the shapes to keep the computation tractable.

I still check on Aude every season now, since it’s where this whole project started: the Aude département in France does show 2026 activity: 1 mapped fire totalling about 600 hectares so far. France as a whole stands at about 40,300 hectares across 1,448 mapped fires this year.

What is actually burning?

Beyond where fire keeps coming back, there is a second question worth asking: is this season eating into different vegetation than usual? To compare fairly, I use identical calendar windows: what burned between 1 June and 5 July this year, against what burned in the very same window in the nine previous seasons pooled together.

Show code
cutoff_md <- format(as_of, "%m-%d")

lc_share <- function(df, label) {
  df |>
    mutate(across(all_of(lc_cols), to_num)) |>
    summarise(across(all_of(lc_cols), ~ sum(.x / 100 * area_ha, na.rm = TRUE))) |>
    pivot_longer(everything(), names_to = "class", values_to = "lc_ha") |>
    mutate(share = lc_ha / sum(lc_ha), group = label)
}

lc_hist <- map_dfr(HIST_YEARS, function(y) {
  get_tagged_window(
    y, snap, eu,
    start_date = as.Date(sprintf("%d-06-01", y)),
    end_date   = as.Date(paste0(y, "-", cutoff_md))
  ) |>
    st_drop_geometry() |>
    mutate(year = y)
})

lc_2026 <- get_tagged_summer(YEAR_CURRENT, snap, eu,
                             SEASON_START_MONTH, SEASON_END_MONTH) |>
  st_drop_geometry() |>
  filter(ba_date <= as_of)

shares_2026 <- lc_share(lc_2026, sprintf("2026 (Jun 1 – %s %d)", month.abb[month(as_of)], day(as_of)))
shares_hist <- lc_share(lc_hist, "2017–2025 average\n(same window)")

lc_comp <- bind_rows(shares_2026, shares_hist) |>
  mutate(
    class = factor(lc_labels[class], levels = names(pal_lc)),
    group = fct_rev(factor(group, levels = unique(group)))
  )

# Largest 2026-vs-history composition gap, quoted (stale-proof) in the prose
lc_gap <- shares_2026 |>
  select(class, share_cur = share) |>
  left_join(shares_hist |> select(class, share_hist = share), by = "class") |>
  mutate(class_label = lc_labels[class], gap = share_cur - share_hist) |>
  slice_max(abs(gap), n = 1)

ggplot(lc_comp, aes(x = share, y = group, fill = class)) +
  geom_col(width = 0.62, color = NA) +
  geom_text(
    aes(
      label = if_else(share >= 0.07, label_percent(accuracy = 1)(share), ""),
      group = class
    ),
    position = position_stack(vjust = 0.5),
    color = "white", size = 3.3, fontface = "bold"
  ) +
  scale_fill_manual(values = pal_lc, breaks = names(pal_lc), name = "Land cover") +
  scale_x_continuous(labels = label_percent(), expand = expansion(mult = c(0, 0.01))) +
  labs(
    x = "Share of burned area", y = NULL,
    caption = "Pooled 2017–2025 shares are area-weighted across all nine years' same-window fires."
  ) +
  theme_burns(base_size = 12)

Land-cover composition of burned area in the 1 June – 5 July window: 2026 versus the pooled (area-weighted) 2017–2025 average for the same window. Shares are each perimeter’s land-cover percentages weighted by its Europe-clipped area. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

Land-cover composition of burned area in the 1 June – 5 July window: 2026 versus the pooled (area-weighted) 2017–2025 average for the same window. Shares are each perimeter’s land-cover percentages weighted by its Europe-clipped area. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

Compare the two bars: where they differ is where this season deviates from the recent norm. Right now the biggest gap is Other natural LC, at 13% of this season’s burned area versus 37% in a typical early season. Read the bars as what burned, not as what burns easily: they describe the landscape the fires happened to cross.

  • Broad-leaved forest: deciduous trees (oaks, beeches).
  • Coniferous forest: needle-leaf trees (pines, spruces, firs), including plantations.
  • Mixed forest: broad-leaved and coniferous stands together.
  • Sclerophyllous vegetation: fire-adapted Mediterranean shrubland (maquis, garrigue); evergreen, waxy leaves.
  • Transitional woodland-shrub: open, regenerating or degraded woodland, shrub mosaics, young stands.
  • Agricultural areas: arable land, permanent crops, pastures.
  • Artificial surfaces: urban and built-up areas, infrastructure.
  • Other natural LC: natural cover not listed above (heath, sparse vegetation, dunes, rocky ground).
  • Other LC: residual or unknown classes.

How much of this is happening in protected nature?

One more lens on what burned: how much of it sits inside land specifically protected for nature?

NoteWhat is Natura 2000?

Natura 2000 is the European Union’s network of protected natural sites, covering habitats and species the EU has agreed are worth safeguarding. It is not a fire ban. Many Natura 2000 landscapes, especially Mediterranean scrub and some forest types, are fire-adapted ecosystems where occasional burning is part of their natural cycle. A fire inside a protected site is not automatically a disaster, but tracking how much burning happens there is still useful context.

EFFIS tags every perimeter with the share of its own area that falls inside a Natura 2000 site. The chart below turns that into a season-by-season series, using the same 1 June cutoff every year so the comparison is fair.

Show code
natura_trend <- build_natura_trend(
  hist_years = HIST_YEARS, year_current = YEAR_CURRENT,
  current_tagged = tagged_2026_full, snapshot_dir = snap, eu = eu, cutoff_md = cutoff_md
)

plot_natura_trend(natura_trend, YEAR_CURRENT)

Share of Europe-clipped burned area falling inside a Natura 2000 protected site, 1 June – 5 July each year, computed as area-weighted PERCNA2K (EFFIS’s own field for the share of each perimeter’s own area inside Natura 2000). The window is identical every year, so the series is not distorted by how much of the season had elapsed. 2026 is highlighted. Source: EFFIS rapid perimeters, snapshots covering 2017–2026.

Share of Europe-clipped burned area falling inside a Natura 2000 protected site, 1 June – 5 July each year, computed as area-weighted PERCNA2K (EFFIS’s own field for the share of each perimeter’s own area inside Natura 2000). The window is identical every year, so the series is not distorted by how much of the season had elapsed. 2026 is highlighted. Source: EFFIS rapid perimeters, snapshots covering 2017–2026.
Show code
natura_2026       <- natura_trend |> dplyr::filter(year == YEAR_CURRENT)
natura_hist_median <- stats::median(natura_trend$share[natura_trend$year != YEAR_CURRENT], na.rm = TRUE)

Weighting by area, about 23% of 2026’s burned area in this window sits inside a Natura 2000 site, against a 30% median for 2017-2025 by the same date. The map below shows exactly which 2026 scars carry a high protected-area share; grey outlines mark fires with none.

Show code
plot_natura_map(tagged_2026_full, eu)

2026 perimeters (1 January – 5 July) colored by PERCNA2K, the share of each fire’s own perimeter that falls inside a Natura 2000 protected site. Grey: 0% (no protected-area overlap). Bright yellow: perimeters that burned almost entirely inside protected land. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

2026 perimeters (1 January – 5 July) colored by PERCNA2K, the share of each fire’s own perimeter that falls inside a Natura 2000 protected site. Grey: 0% (no protected-area overlap). Bright yellow: perimeters that burned almost entirely inside protected land. Source: EFFIS rapid perimeters, snapshot 2026-07-06.

That is the season through 5 July. The real test is still ahead: most of a typical European fire season happens in July and August, and this page updates weekly as new EFFIS outlines come in. Watch, above all, whether the elevated pace from this first month holds, eases, or turns into something worse.

NoteA final word of caution

Everything on this page is a rapid satellite estimate, not an official statistic. Full methods and caveats are on the About page.