Rent Control and Housing Policy

The appeal and the problem

Rent control caps how much landlords can charge or how fast rents can rise. Its goal is clear: protect tenants from sharp rent increases and let them stay in their homes. The problem is that a price cap changes what landlords do. Over time, some units leave the rental market, and fewer new ones get built.

A price ceiling

In the simplest model, the rental market has a demand curve and a supply curve. The market rent is where they cross. A rent ceiling below the market rent does three things:

  1. Tenants who keep a unit gain. They pay the gap between the market rent and the ceiling less each month.
  2. Fewer units are offered. At the lower rent, landlords supply fewer units than before.
  3. A shortage appears. At the low rent, more people want units than are available.

Some trades that would help both sides no longer happen. That lost value is the deadweight loss.

Time matters

In the short run, the housing stock is fixed. Supply is steep, so a ceiling mostly moves money from landlords to tenants. In the long run, landlords can convert units to condos, sell to owner-occupiers, or skip maintenance. Supply becomes flatter, and the losses grow.

Diamond, McQuade and Qian (2019) found exactly this in San Francisco. Tenants covered by the 1994 expansion of rent control were more likely to stay at their address. But landlords of covered buildings cut the supply of rental housing by 15 percent, often by converting to condos. That pushed up rents for everyone else.

Other tools

Economists often prefer other ways to help renters:

  • Housing vouchers pay part of the rent for low-income households. They help without cutting supply, though they can push up rents if supply is fixed.
  • More supply, through zoning reform, lowers rents for everyone. See Zoning & Housing Supply.
  • Targeted protections, like limits on very large one-year increases, aim at the main fear behind rent control with less effect on supply.

The Model View. Set the rent ceiling and how flexible supply is. Steep supply is the short run. Flat supply is the long run. The plot shows the shortage and the deadweight loss. The bars show who gains and who loses.

#| standalone: true
#| viewerHeight: 640

library(shiny)

ui <- fluidPage(
  tags$head(tags$style(HTML("
    .info-box {
      background: #eaf2f8; border-radius: 6px; padding: 14px;
      margin-top: 12px; font-size: 13px; line-height: 1.8;
    }
    .info-box b { color: #2c3e50; }
  "))),

  sidebarLayout(
    sidebarPanel(
      width = 3,
      sliderInput("ceiling", "Rent ceiling (% of market rent):",
                  min = 50, max = 100, value = 80, step = 5),
      sliderInput("elast", "Supply elasticity (low = short run, high = long run):",
                  min = 0.1, max = 3, value = 0.5, step = 0.1),
      uiOutput("info")
    ),
    mainPanel(
      width = 9,
      fluidRow(
        column(7, plotOutput("market_plot", height = "480px")),
        column(5, plotOutput("bar_plot", height = "480px"))
      )
    )
  )
)

server <- function(input, output, session) {

  Rm <- 2000      # market rent, $ per month
  Qm <- 100       # thousand units at the market rent
  ed <- 0.5       # demand elasticity at the market point

  model <- reactive({
    es <- input$elast
    # linear curves through (Qm, Rm) with the chosen elasticities
    bd <- ed * Qm / Rm; ad <- Qm + bd * Rm      # Qd = ad - bd R
    bs <- es * Qm / Rm; as <- Qm - bs * Rm      # Qs = as + bs R
    Rc <- Rm * input$ceiling / 100
    Qs <- max(as + bs * Rc, 0)
    Qd <- ad - bd * Rc
    inv_d <- function(Q) (ad - Q) / bd           # willingness to pay
    inv_s <- function(Q) (Q - as) / bs           # marginal cost of supply
    # surplus changes, $ thousands per month (units in thousands)
    dwl <- if (Qs < Qm) integrate(function(Q) inv_d(Q) - pmax(inv_s(Q), 0), Qs, Qm)$value else 0
    tenant_transfer <- (Rm - Rc) * Qs
    tenant_lost <- if (Qs < Qm) integrate(function(Q) inv_d(Q) - Rm, Qs, Qm)$value else 0
    landlord_loss <- tenant_transfer + if (Qs < Qm) integrate(function(Q) Rm - pmax(inv_s(Q), 0), Qs, Qm)$value else 0
    list(es = es, ad = ad, bd = bd, as = as, bs = bs, Rc = Rc, Qs = Qs, Qd = Qd,
         inv_d = inv_d, inv_s = inv_s, dwl = dwl,
         tenant_net = tenant_transfer - tenant_lost, landlord_loss = landlord_loss)
  })

  output$market_plot <- renderPlot({
    m <- model()
    Q <- seq(0, 180, length.out = 300)
    par(mar = c(4.5, 4.5, 3, 1))
    plot(Q, m$inv_d(Q), type = "l", lwd = 3, col = "#2c3e50",
         ylim = c(0, 4000), xlim = c(0, 180),
         xlab = "Rental units (thousands)", ylab = "Monthly rent ($)",
         main = "The rental market with a ceiling")
    qs_line <- Q[Q >= max(m$as, 0)]
    lines(qs_line, m$inv_s(qs_line), lwd = 3, col = "#3498db")
    # deadweight loss triangle
    if (m$Qs < Qm) {
      qq <- seq(m$Qs, Qm, length.out = 60)
      polygon(c(qq, rev(qq)), c(m$inv_d(qq), rev(pmax(m$inv_s(qq), 0))),
              col = adjustcolor("#e74c3c", 0.3), border = NA)
    }
    abline(h = m$Rc, lwd = 2, lty = 2, col = "#e67e22")
    abline(h = Rm, lwd = 1, lty = 3, col = "#7f8c8d")
    # shortage bracket
    if (m$Qd > m$Qs) {
      arrows(m$Qs, m$Rc - 150, m$Qd, m$Rc - 150, code = 3, length = 0.08, col = "#e67e22", lwd = 2)
      text((m$Qs + m$Qd) / 2, m$Rc - 300, "Shortage", col = "#e67e22", cex = 0.9)
    }
    legend("topright", bty = "n", cex = 0.85,
           legend = c("Demand", "Supply", "Rent ceiling", "Market rent", "Deadweight loss"),
           col = c("#2c3e50", "#3498db", "#e67e22", "#7f8c8d", adjustcolor("#e74c3c", 0.5)),
           lwd = c(3, 3, 2, 1, 10), lty = c(1, 1, 2, 3, 1))
  })

  output$bar_plot <- renderPlot({
    m <- model()
    vals <- c(m$tenant_net, -m$landlord_loss, -m$dwl) / 1000
    par(mar = c(6, 4.5, 3, 1))
    b <- barplot(vals, names.arg = c("Tenants\n(net)", "Landlords", "Lost value\n(DWL)"),
                 col = c("#27ae60", "#e74c3c", "#7f8c8d"),
                 ylab = "$ millions per month", main = "Who gains and who loses",
                 ylim = c(-1, 1) * max(10, 1.2 * max(abs(vals))))
    abline(h = 0)
    off <- 0.06 * max(10, max(abs(vals)))
    text(b, vals + ifelse(vals >= 0, off, -off), sprintf("%.1f", vals), cex = 0.9)
  })

  output$info <- renderUI({
    m <- model()
    tags$div(class = "info-box",
      HTML(paste0(
        "<b>Controlled rent:</b> $", format(round(m$Rc), big.mark = ","), "<br>",
        "<b>Units supplied:</b> ", round(m$Qs, 1), "k (was ", Qm, "k)<br>",
        "<b>Units wanted:</b> ", round(m$Qd, 1), "k<br>",
        "<b>Shortage:</b> ", round(max(m$Qd - m$Qs, 0), 1), "k units<br>",
        "<b>Deadweight loss:</b> $", sprintf("%.1f", m$dwl / 1000), "M per month"
      ))
    )
  })
}

shinyApp(ui, server)

Things to try

  • Short run: set supply elasticity to 0.1. Supply barely falls. The ceiling mostly moves money from landlords to tenants, and the lost value is small.
  • Long run: raise supply elasticity to 2 or more. Now many units leave the market, the shortage grows, and the lost value is large.
  • Tighten the ceiling: move it from 90% to 60% of market rent. The lost value grows faster than the cut in rent. Deadweight loss grows with the square of the gap.
  • Watch the tenant bar: tenants who keep a unit gain, but tenants who cannot find one lose. With flexible supply, tenants as a group can end up worse off.

What the simple model leaves out

Real rent control is messier than the model. It usually exempts new buildings, so it may not stop new construction. It also keeps tenants in units that no longer fit them, because moving means losing the low rent.

Who pays matters too. Ahern and Giacoletti (2022) found that after St. Paul, Minnesota, passed rent control in 2021, property values fell, and some of the gains went to tenants who were not poor.

Connections

Did you know?

  • New York City has had some form of rent regulation since World War II, when federal wartime controls began.
  • In 1994, Massachusetts voters ended rent control statewide by a narrow margin. Autor, Palmer and Pathak (2014) used this to study Cambridge, where controlled and uncontrolled buildings sat side by side. Values of never-controlled homes near controlled ones rose after decontrol.
  • Rent control is one of the few policies where economists across the political spectrum largely agree. Most argue it reduces the supply and quality of rental housing over time.

Key papers