Dutch Property Values: A Municipal Analysis

Analyzing WOZ Trends Across the Netherlands (2016 - 2024)

Author

Reproducible Research Demo

Published

January 29, 2026

Code
library(data.table)
library(ggplot2)
library(sf)
library(fixest)

# Defines START_YEAR and END_YEAR
source("scripts/constants.R")

# Define major (G4) cities with their gemeente codes
G4_CITIES = data.table::rowwiseDT(
  region=, name=,color=,
  "GM0363", "Amsterdam", "#1b9e77",
  "GM0599", "Rotterdam", "#d95f02",
  "GM0518", "Den Haag", "#7570b3",
  "GM0344", "Utrecht", "#e7298a"
)
WarningNot a real research!

Don’t mind the narrative but the data is real.

1 Introduction

The Dutch housing market has experienced remarkable changes over the past decade. Property values, as measured by the WOZ (Waardering Onroerende Zaken) valuations, serve as a key indicator of real estate market dynamics (Vries et al. 2009). These official government valuations are used for property taxation and reflect market conditions across municipalities.

This analysis examines the evolution of property values across Dutch municipalities using data from Statistics Netherlands (CBS). We address the research question:

How have property values evolved across Dutch municipalities since 2016, and what spatial patterns emerge?

Our analysis uses R for data processing, statistical modeling, and visualization, demonstrating Quarto’s capabilities for reproducible research.

2 Data

We use the CBS Kerncijfers Wijken en Buurten (Key Figures for Neighborhoods and Districts) dataset (Statistics Netherlands (CBS) 2024), which provides annual statistics at the municipality (gemeente) level.

# Load CBS housing data
housing = fread(file.path("data", "processed", "kwb_housing.csv"))
# Keep only from START_YEAR to END_YEAR
housing = housing[year >= START_YEAR & year <= END_YEAR, ]
# Filter to gemeente level with valid WOZ data
housing_municip = housing[region_type == "gemeente" & !is.na(woz), ]

# Summary statistics
n_gem = uniqueN(housing_municip$region)
year_range = range(housing_municip$year)
n_obs = nrow(housing_municip)
Code
##| eval: !expr 'START_YEAR != year_range[1] || END_YEAR != year_range[2]'

# Redefine start and end year based on data
start_year_old = START_YEAR
end_year_old = END_YEAR
START_YEAR = max(year_range[1], START_YEAR)
END_YEAR = min(year_range[2], END_YEAR)
if (START_YEAR != start_year_old || END_YEAR != end_year_old) {
  cat(sprintf('WARNING: [Adjusted analysis period to %s-%s based on data availability, you defined [%s, %s].]{style="color:red"}', START_YEAR, END_YEAR, start_year_old, end_year_old))
}

The dataset contains 3,236 observations covering 408 municipalities from 2016 to 2024.

Code
summary_by_year = housing_municip[, .(
  Municipalities = uniqueN(region),
  `Mean WOZ (€1000)` = sprintf("%.2f", mean(woz, na.rm = TRUE)),
  `Median WOZ` = sprintf("%.2f", median(woz, na.rm = TRUE)),
  `Min WOZ` = min(woz, na.rm = TRUE),
  `Max WOZ` = max(woz, na.rm = TRUE)
), by = year]

knitr::kable(summary_by_year)
Table 1: Data summary by year
year Municipalities Mean WOZ (€1000) Median WOZ Min WOZ Max WOZ
2016 390 222.15 219.50 122 554
2017 380 226.96 224.00 121 588
2018 375 237.19 233.00 126 637
2019 355 258.33 250.00 131 681
2020 355 277.07 266.00 138 735
2021 352 296.08 284.50 148 775
2022 345 324.37 314.00 167 829
2023 342 374.01 363.00 195 925
2024 342 387.80 380.00 199 900

Beyond WOZ values, the dataset includes housing stock composition (see Figure 4 in the Section 8).

4 Regression Analysis

To estimate the average annual growth rate across municipalities while controlling for municipal fixed effects, we estimate a panel regression model. Similar approaches using hedonic pricing models have been applied to Dutch real estate appraisals (Guliker, Folmer, and Sinderen 2022). We estimate:

\[ \log(\text{WOZ}_{it}) = \alpha + \beta \cdot t + \gamma_i + \varepsilon_{it} \tag{2}\]

where:

  • \(\text{WOZ}_{it}\) is the WOZ value for municipality \(i\) in year \(t\)
  • \(\beta\) captures the average annual growth rate (in log points)
  • \(\gamma_i\) are municipality fixed effects
  • \(\varepsilon_{it}\) is the error term
Code
# Prepare panel data
panel = copy(housing_municip)
panel[, `:=`(
  log_woz = log(woz * 1000),
  year_centered = year - START_YEAR  # Center year for interpretation
)]

# Fixed effects regression
fe_model = feols(log_woz ~ year_centered | region, data = panel)

# Extract coefficient
beta = coef(fe_model)["year_centered"]
annual_growth_pct = (exp(beta) - 1) * 100

The regression results indicate an average annual growth rate of 7.6% in WOZ values across Dutch municipalities (see Equation 2). This estimate is based on 3,233 observations across 408 municipalities.

Code
# Summary table
etable(fe_model, se = "cluster", cluster = ~region, tex = knitr::is_latex_output(),
      dict = c(year_centered = sprintf("Year (from %s)", START_YEAR)))
Table 3: Panel Regression Results
                           fe_model
Dependent Var.:             log_woz
                                   
Year (from 2016) 0.0733*** (0.0006)
Fixed-Effects:   ------------------
region                          Yes
________________ __________________
S.E.: Clustered          by: region
Observations                  3,233
R2                          0.97703
Within R2                   0.93980
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

5 Spatial Distribution

Figure 2 shows the geographic distribution of WOZ values across Dutch municipalities in 2024.

Code
# Read spatial data
gem_sf = st_read("data/geodata/gemeenten_2024.gpkg", quiet = TRUE)

# Prepare WOZ data for end year
woz_end = housing_municip[year == END_YEAR, .(gemeentecode = region, woz)]

# Join spatial and attribute data
map_data = merge(gem_sf, woz_end, by = "gemeentecode", all.x = TRUE)

# Create choropleth map
ggplot(map_data) +
  geom_sf(aes(fill = woz), color = "white", linewidth = 0.1) +
  scale_fill_viridis_c(
    option = "plasma",
    na.value = "grey80",
    labels = scales::label_number(suffix = "k"),
    guide = guide_colorbar(
      title = "WOZ (€1000)",
      title.position = "top",
      barwidth = 15,
      barheight = 0.5
    )
  ) +
  labs(
    title = "Property Values Across Dutch Municipalities",
    subtitle = paste("WOZ valuations in", END_YEAR)
  ) +
  theme_void(base_size = 12) +
  theme(
    legend.position = "bottom",
    plot.title = element_text(face = "bold", hjust = 0.5),
    plot.subtitle = element_text(hjust = 0.5, color = "grey40")
  )
Figure 2: WOZ Values by Municipality (2024)

The map Figure 2 reveals clear spatial patterns: the Randstad region (Amsterdam, Utrecht, The Hague) and areas around Haarlem show the highest property values, while peripheral regions in the north and east have lower valuations (Claassens, Koomen, and Rouwendal 2020).

6 Compound Annual Growth Rate (CAGR) Analysis

Beyond simple growth rates, we can calculate the Compound Annual Growth Rate [CAGR; Cipra (2010)], which provides a smoothed annual rate of return:

\[ \text{CAGR} = \left( \frac{\text{WOZ}_{t_1}}{\text{WOZ}_{t_0}} \right)^{1/n} - 1 \]

where \(n\) = 8 years.

Note

The following figure is embedded from a separate analysis document using Quarto’s embed shortcode, demonstrating modular workflows:

Figure 3: Distribution of Compound Annual Growth Rates
Source: WOZ Summary Statistics

7 Conclusion

This analysis of Dutch property values reveals several key findings:

  1. Substantial growth: WOZ values increased by an average of 7.6% annually between 2016 and 2024
  2. Urban premium: Major cities, particularly Amsterdam, maintain significantly higher property values (Musterd, Hochstenbach, and Boterman 2020)
  3. Spatial patterns: Clear geographic clustering with highest values in the Randstad region

These insights have implications for policymakers, urban planners, and real estate stakeholders in understanding market dynamics and guiding future development.

References

Cipra, Tomas. 2010. Financial and Insurance Formulas. Physica-Verlag HD. https://doi.org/10.1007/978-3-7908-2593-0.
Claassens, Jip, Eric Koomen, and Jan Rouwendal. 2020. “Urban Density and Spatial Planning: The Unforeseen Impacts of Dutch Devolution.” Edited by Eda Ustaoglu. PLOS ONE 15 (10): e0240738. https://doi.org/10.1371/journal.pone.0240738.
Guliker, Evert, Erwin Folmer, and Marten van Sinderen. 2022. “Spatial Determinants of Real Estate Appraisals in the Netherlands: A Machine Learning Approach.” ISPRS International Journal of Geo-Information 11 (2): 125. https://doi.org/10.3390/ijgi11020125.
Musterd, Sako, Cody Hochstenbach, and Willem Boterman. 2020. “Ripples of Structural Economic Transformation: The Changing Social Geographies of Dutch Metropolitan Regions.” Applied Geography 116 (March): 102151. https://doi.org/10.1016/j.apgeog.2020.102151.
Statistics Netherlands (CBS). 2024. “Kerncijfers Wijken En Buurten.” https://www.cbs.nl/nl-nl/reeksen/kerncijfers-wijken-en-buurten.
Vries, Paul de, Jan de Haan, Erna van der Wal, and Gust Mariën. 2009. “A House Price Index Based on the SPAR Method.” Journal of Housing Economics 18 (3): 214–23. https://doi.org/10.1016/j.jhe.2009.07.002.

8 Appendix