Skip to contents

New Hampshire’s public schools enrolled 160,322 students in 2025-26 — down 16% from 190,805 in 2011-12. This package provides 15 years of enrollment data from the NH Department of Education, covering ~200 districts and ~500 schools.

Part of the njschooldata family.

Full documentation — all 15 stories with interactive charts, getting-started guide, and complete function reference.

Highlights

1. NH lost 30,000 students since 2012

State enrollment fell from 190,805 to 160,322 — a 16% decline driven by demographic contraction in one of America’s oldest-population states.

library(nhschooldata)
library(dplyr)
library(ggplot2)

enr <- fetch_enr_multi(2012:2026, use_cache = TRUE)

state_trend <- enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, n_students) |>
  arrange(end_year)

stopifnot(nrow(state_trend) > 0)

state_trend |>
  mutate(
    change = n_students - lag(n_students),
    cumulative_change = n_students - first(n_students)
  )
#> # A tibble: 15 × 4
#>    end_year n_students change cumulative_change
#>       <int>      <int>  <int>             <int>
#>  1     2012     190805     NA                 0
#>  2     2013     187962  -2843             -2843
#>  3     2014     185320  -2642             -5485
#>  4     2015     183604  -1716             -7201
#>  5     2016     181339  -2265             -9466
#>  6     2017     179734  -1605            -11071
#>  7     2018     178328  -1406            -12477
#>  8     2019     177365   -963            -13440
#>  9     2020     176168  -1197            -14637
#> 10     2021     167909  -8259            -22896
#> 11     2022     168620    711            -22185
#> 12     2023     167357  -1263            -23448
#> 13     2024     165082  -2275            -25723
#> 14     2025     162660  -2422            -28145
#> 15     2026     160322  -2338            -30483
State enrollment trend
State enrollment trend

(source)

2. Charter schools grew from 0.6% to 3.9% of enrollment

While total enrollment shrank, charter schools quintupled their share — from 9 charters with 1,090 students in 2012 to 35 charters with 6,242 in 2026.

charter_names <- enr |>
  filter(is_district, end_year == 2026) |>
  filter(grepl("Charter|Chartered", district_name, ignore.case = TRUE)) |>
  distinct(district_name) |>
  pull()

charter_trend <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         district_name %in% charter_names) |>
  group_by(end_year) |>
  summarize(
    n_charter = sum(n_students, na.rm = TRUE),
    n_charters = n(),
    .groups = "drop"
  )

state_total <- enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, state_total = n_students)

charter_pct <- charter_trend |>
  left_join(state_total, by = "end_year") |>
  mutate(pct = round(n_charter / state_total * 100, 1))

stopifnot(nrow(charter_pct) > 0)
charter_pct
#> # A tibble: 15 × 5
#>    end_year n_charter n_charters state_total   pct
#>       <int>     <int>      <int>       <int> <dbl>
#>  1     2012      1090          9      190805   0.6
#>  2     2013      1640         14      187962   0.9
#>  3     2014      1978         15      185320   1.1
#>  4     2015      2426         19      183604   1.3
#>  5     2016      2890         21      181339   1.6
#>  6     2017      3297         21      179734   1.8
#>  7     2018      3421         21      178328   1.9
#>  8     2019      3752         23      177365   2.1
#>  9     2020      3993         23      176168   2.3
#> 10     2021      4336         24      167909   2.6
#> 11     2022      4756         25      168620   2.8
#> 12     2023      5211         27      167357   3.1
#> 13     2024      5460         29      165082   3.3
#> 14     2025      5938         31      162660   3.7
#> 15     2026      6242         35      160322   3.9
Charter school growth
Charter school growth

(source)

3. COVID erased 8,259 students in a single year

The 2020-21 school year saw a 4.7% enrollment drop — by far the largest single-year decline in the dataset. NH has not recovered.

covid <- enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL",
         end_year %in% 2019:2022) |>
  select(end_year, n_students) |>
  arrange(end_year) |>
  mutate(
    yoy_change = n_students - lag(n_students),
    yoy_pct = round((n_students / lag(n_students) - 1) * 100, 1)
  )

stopifnot(nrow(covid) == 4)
covid
#> # A tibble: 4 × 4
#>   end_year n_students yoy_change yoy_pct
#>      <int>      <int>      <int>   <dbl>
#> 1     2019     177365         NA    NA
#> 2     2020     176168      -1197    -0.7
#> 3     2021     167909      -8259    -4.7
#> 4     2022     168620        711     0.4
Year-over-year change
Year-over-year change

(source)

Data Taxonomy

Category Years Function Details
Enrollment 2012-2026 fetch_enr() / fetch_enr_multi() State, district, school. Grade-level totals
Assessments Not yet available
Graduation Not yet available
Directory Current fetch_directory() Schools, SAUs. Address, phone, principal, superintendent
Per-Pupil Spending Not yet available
Accountability Not yet available
Chronic Absence Not yet available
EL Progress Not yet available
Special Ed Not yet available

See the full data category taxonomy

Quick Start

R

remotes::install_github("almartin82/nhschooldata")

library(nhschooldata)
library(dplyr)

# Fetch enrollment for 2025-26
enr <- fetch_enr(2026)

# State total
enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  select(end_year, n_students)

# Top 5 districts
enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL") |>
  arrange(desc(n_students)) |>
  select(district_name, n_students) |>
  head(5)

Python

pip install git+https://github.com/almartin82/nhschooldata.git#subdirectory=pynhschooldata
from pynhschooldata import fetch_enr

enr = fetch_enr(2026)
print(enr.head())

Explore More

Full analysis with 15 stories: - Enrollment trends — 15 stories covering state trends, district deep dives, grade-level patterns, and structural analysis - Function reference

Data Notes

Item Detail
Source NH DOE iPlatform
Years 2012-2026 (15 school years)
Entity types ~200 districts (organized into SAUs), ~500 schools
Census day October 1 of each school year
Demographics Not available in standard enrollment reports (grade totals only)
Suppression Counts < 10 may be suppressed with *
Download method Headed Playwright (Akamai WAF blocks headless HTTP)

Deeper Dive

4. Manchester lost nearly a quarter of its students

New Hampshire’s largest city saw enrollment drop from 15,536 to 11,712 — a loss of 3,824 students (24.6%) while maintaining 20 schools.

big2 <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         district_name %in% c("Manchester", "Nashua")) |>
  select(end_year, district_name, n_students) |>
  arrange(end_year, district_name)

stopifnot(nrow(big2) > 0)
big2 |>
  filter(end_year %in% c(2012, 2018, 2026)) |>
  pivot_wider(names_from = district_name, values_from = n_students)
#> # A tibble: 3 × 3
#>   end_year Manchester Nashua
#>      <int>      <int>  <int>
#> 1     2012      15536  11894
#> 2     2018      13477  10598
#> 3     2026      11712   9501
Manchester vs Nashua
Manchester vs Nashua

(source)

5. Errol has 12 students — the state’s tiniest district

New Hampshire has dozens of districts with fewer than 100 students. The smallest, Errol, has just 12 — smaller than most college seminar classes.

smallest <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         end_year == 2026) |>
  select(district_name, sau, sau_name, n_students) |>
  arrange(n_students) |>
  head(10)

stopifnot(nrow(smallest) > 0)
smallest
#> # A tibble: 10 × 4
#>    district_name                                     sau   sau_name  n_students
#>    <chr>                                             <chr> <chr>          <int>
#>  1 Errol                                             20    Gorham            12
#>  2 Landaff                                           35    SAU #35…         15
#>  3 North Star Academy Chartered Public School        406   North S…         17
#>  4 Jackson                                           9     Conway           26
#>  5 Croydon                                           99    Croydon          28
#>  6 Stark                                             58    Groveton         30
#>  7 Marlow                                            29    Keene            32
#>  8 CSI Charter School                                410   CSI Cha…         32
#>  9 NH Career Academy Chartered Public School         421   NH Care…         32
#> 10 Waterville Valley                                 48    Plymouth         34
Smallest districts
Smallest districts

(source)

6. Virtual Learning Academy grew 756% — the biggest gainer

While most districts shrank, Virtual Learning Academy Charter School grew from 63 students in 2012 to 539 in 2026 — a 756% increase.

changes <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         end_year %in% c(2012, 2026)) |>
  select(end_year, district_name, n_students) |>
  pivot_wider(names_from = end_year, values_from = n_students,
              names_prefix = "y") |>
  filter(!is.na(y2012), !is.na(y2026)) |>
  mutate(
    change = y2026 - y2012,
    pct_change = round((y2026 / y2012 - 1) * 100, 1)
  )

stopifnot(nrow(changes) > 0)
cat("Top 5 gainers:\n")
changes |> arrange(desc(pct_change)) |> head(5)
#> Top 5 gainers:
#> # A tibble: 5 × 5
#>   district_name                                     y2012 y2026 change pct_change
#>   <chr>                                             <int> <int>  <int>      <dbl>
#> 1 Virtual Learning Academy Charter School              63   539    476      755.6
#> 2 Academy for Science and Design Charter School       285   671    386      135.4
#> 3 Strong Foundations Charter School                   172   336    164       95.3
#> 4 Gate City Charter School For the Arts               106   203     97       91.5
#> 5 Making Community Connections Charter School          97   171     74       76.3
Biggest gainers
Biggest gainers

(source)

7. Top 5 losers account for 9,481 lost students

The five largest districts — Manchester, Nashua, Hudson, Concord, and Londonderry — together lost 9,481 students, nearly a third of the statewide decline.

top_losers <- changes |>
  arrange(change) |>
  head(5)

stopifnot(nrow(top_losers) == 5)
top_losers
#> # A tibble: 5 × 5
#>   district_name y2012 y2026 change pct_change
#>   <chr>         <int> <int>  <int>      <dbl>
#> 1 Manchester    15536 11712  -3824      -24.6
#> 2 Nashua        11894  9501  -2393      -20.1
#> 3 Hudson         4052  2875  -1177      -29.0
#> 4 Concord        4842  3755  -1087      -22.4
#> 5 Londonderry    4847  3847  -1000      -20.6
Top losers trend
Top losers trend

(source)

8. Grade 12 consistently outnumbers Grade 1

Every year since 2012, more students graduate from 12th grade than enter 1st grade — a demographic signature of sustained population decline.

pipeline <- enr |>
  filter(is_campus, subgroup == "total_enrollment",
         grade_level %in% c("01", "12")) |>
  group_by(end_year, grade_level) |>
  summarize(n = sum(n_students, na.rm = TRUE), .groups = "drop") |>
  pivot_wider(names_from = grade_level, values_from = n) |>
  rename(grade_01 = `01`, grade_12 = `12`) |>
  mutate(ratio = round(grade_12 / grade_01, 2))

stopifnot(nrow(pipeline) > 0)
pipeline
#> # A tibble: 15 × 4
#>    end_year grade_01 grade_12 ratio
#>       <int>    <int>    <int> <dbl>
#>  1     2012    13595    14673  1.08
#>  2     2013    13609    14404  1.06
#>  3     2014    13461    13962  1.04
#>  4     2015    13157    13671  1.04
#>  5     2016    12898    13752  1.07
#>  6     2017    12377    13338  1.08
#>  7     2018    12678    13235  1.04
#>  8     2019    12351    13080  1.06
#>  9     2020    12501    13172  1.05
#> 10     2021    11675    13114  1.12
#> 11     2022    11754    12867  1.09
#> 12     2023    12099    12471  1.03
#> 13     2024    11687    12502  1.07
#> 14     2025    11278    12493  1.11
#> 15     2026    11169    12388  1.11
Pipeline ratio
Pipeline ratio

(source)

9. PreK enrollment grew 39% while everything else shrank

PreK enrollment rose from 3,165 to 4,395 — a 39% increase — even as overall enrollment fell 16%.

prek <- enr |>
  filter(is_state, subgroup == "total_enrollment", grade_level == "PK") |>
  select(end_year, n_students) |>
  arrange(end_year)

stopifnot(nrow(prek) > 0)
prek
#> # A tibble: 15 × 2
#>    end_year n_students
#>       <int>      <int>
#>  1     2012       3165
#>  2     2013       3200
#>  3     2014       3401
#>  4     2015       3557
#>  5     2016       3670
#>  6     2017       3894
#>  7     2018       3876
#>  8     2019       4192
#>  9     2020       4518
#> 10     2021       2908
#> 11     2022       3848
#> 12     2023       4385
#> 13     2024       4440
#> 14     2025       4385
#> 15     2026       4395
PreK trend
PreK trend

(source)

10. Kindergarten lost 1,177 students since 2012

Kindergarten enrollment dropped from 11,904 to 10,727 — a 10% decline that signals continued enrollment losses ahead.

k_trend <- enr |>
  filter(is_campus, subgroup == "total_enrollment", grade_level == "K") |>
  group_by(end_year) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop")

stopifnot(nrow(k_trend) > 0)
k_trend
#> # A tibble: 15 × 2
#>    end_year n_students
#>       <int>      <int>
#>  1     2012      11904
#>  2     2013      11888
#>  3     2014      11602
#>  4     2015      11570
#>  5     2016      11187
#>  6     2017      11422
#>  7     2018      11415
#>  8     2019      11691
#>  9     2020      11689
#> 10     2021      10111
#> 11     2022      11212
#> 12     2023      11074
#> 13     2024      10893
#> 14     2025      10871
#> 15     2026      10727
Kindergarten trend
Kindergarten trend

(source)

11. 111 one-school districts vs Manchester’s 20

Over half of NH’s districts have just one school. Meanwhile Manchester operates 20 — nearly as many as the bottom 40 districts combined.

schools_per <- enr |>
  filter(is_campus, subgroup == "total_enrollment", grade_level == "TOTAL",
         end_year == 2026) |>
  group_by(district_name) |>
  summarize(n_schools = n(), .groups = "drop") |>
  arrange(desc(n_schools))

stopifnot(nrow(schools_per) > 0)
cat("Districts by school count:\n")
schools_per |>
  mutate(category = case_when(
    n_schools == 1 ~ "1 school",
    n_schools <= 3 ~ "2-3 schools",
    n_schools <= 10 ~ "4-10 schools",
    TRUE ~ "11+ schools"
  )) |>
  count(category)
#> Districts by school count:
#> # A tibble: 4 × 2
#>   category        n
#>   <chr>       <int>
#> 1 1 school      111
#> 2 11+ schools     5
#> 3 2-3 schools    46
#> 4 4-10 schools   41
School counts
School counts

(source)

12. The SAU system: one administrator, multiple districts

New Hampshire’s School Administrative Units (SAUs) are a unique feature. Plymouth SAU #48 covers 8 districts, allowing tiny towns to share administrative costs.

sau_multi <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         end_year == 2026) |>
  group_by(sau, sau_name) |>
  summarize(
    n_districts = n(),
    total_students = sum(n_students, na.rm = TRUE),
    districts = paste(district_name, collapse = ", "),
    .groups = "drop"
  ) |>
  arrange(desc(n_districts))

stopifnot(nrow(sau_multi) > 0)
sau_multi |> head(8)
#> # A tibble: 8 × 5
#>   sau   sau_name              n_districts total_students districts
#>   <chr> <chr>                       <int>          <int> <chr>
#> 1 48    Plymouth                        8           1753 Ashland, Campton, …
#> 2 16    Exeter                          7           4147 Brentwood, East Ki…
#> 3 29    Keene                           7           3583 Chesterfield, Keene…
#> 4 21    Winnacunnet                     5           2044 Hampton, Hampton F…
#> 5 35    SAU #35 Office                  5            726 Bethlehem, Landaff…
#> 6 53    Pembroke                        5           2695 Allenstown, Chiches…
#> 7 23    Haverhill Cooperative           4            771 Bath, Benton, Haver…
#> 8 24    Henniker                        4           1869 Henniker, John Star…
SAU system
SAU system

(source)

13. North Country lost 26% of its students

The remote northern districts (Coos County area) saw enrollment drop from 2,526 to 1,859 — a 26% decline, steeper than the statewide average.

north_country <- c("Berlin", "Gorham", "Milan", "Errol", "Pittsburg",
                    "Colebrook", "Stark", "Stratford", "Stewartstown",
                    "Northumberland", "Lancaster", "Whitefield", "Dalton")

nc_trend <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         district_name %in% north_country) |>
  group_by(end_year) |>
  summarize(
    n_students = sum(n_students, na.rm = TRUE),
    n_districts = n(),
    .groups = "drop"
  )

stopifnot(nrow(nc_trend) > 0)
nc_trend
#> # A tibble: 15 × 3
#>    end_year n_students n_districts
#>       <int>      <int>       <int>
#>  1     2012       2526           9
#>  2     2013       2481           9
#>  3     2014       2440           9
#>  4     2015       2360           9
#>  5     2016       2299           9
#>  6     2017       2282           9
#>  7     2018       2212           9
#>  8     2019       2102           9
#>  9     2020       2085           9
#> 10     2021       1999           9
#> 11     2022       1984           9
#> 12     2023       1977           9
#> 13     2024       1886           9
#> 14     2025       1858           9
#> 15     2026       1859           9
North Country
North Country

(source)

14. Southern NH’s Boston corridor also losing students

Even districts near the Massachusetts border — traditionally NH’s growth engine — are declining. Salem, Windham, Londonderry, and Hudson together lost 3,432 students.

south_nh <- c("Salem", "Windham", "Londonderry", "Derry", "Hudson",
              "Pelham", "Hampstead", "Atkinson", "Plaistow", "Sandown")

snh_trend <- enr |>
  filter(is_district, subgroup == "total_enrollment", grade_level == "TOTAL",
         district_name %in% south_nh) |>
  group_by(end_year) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop")

stopifnot(nrow(snh_trend) > 0)
snh_trend |> filter(end_year %in% c(2012, 2020, 2026))
#> # A tibble: 3 × 2
#>   end_year n_students
#>      <int>      <int>
#> 1     2012      18887
#> 2     2020      16638
#> 3     2026      15455
Regional comparison
Regional comparison

(source)

15. The grade pyramid: where NH’s students are

The 2025-26 grade distribution shows that upper grades have more students than lower grades — confirming the downward demographic trend. Grade 9 has the most students (13,156), while Grade 1 has the fewest K-12 (11,169).

grade_dist <- enr |>
  filter(is_campus, subgroup == "total_enrollment",
         end_year == 2026,
         !grade_level %in% c("TOTAL", "ELEM", "MIDDLE", "HIGH")) |>
  group_by(grade_level) |>
  summarize(n_students = sum(n_students, na.rm = TRUE), .groups = "drop") |>
  arrange(grade_level)

stopifnot(nrow(grade_dist) > 0)
grade_dist
#> # A tibble: 15 × 2
#>    grade_level n_students
#>    <chr>            <int>
#>  1 01               11169
#>  2 02               11331
#>  3 03               11677
#>  4 04               12160
#>  5 05               11983
#>  6 06               12095
#>  7 07               12311
#>  8 08               12256
#>  9 09               13156
#> 10 10               12213
#> 11 11               12339
#> 12 12               12388
#> 13 K                10727
#> 14 PG                  73
#> 15 PK                4395
Grade pyramid
Grade pyramid

(source)