From 2017 to 2024, youth migration traces a rhythm of hope, disruption, and return. For many young Tasmanians, leaving isn’t rebellion, it’s survival. The 20–24 group drives that story: chasing study, work, and momentum the island rarely offers. When borders closed, home drew them back; but as soon as the world reopened, the familiar pull outward resumed. The data shows what many of us already feel that opportunity still lives elsewhere, and Tasmania keeps losing its future one departure at a time.
From 2017 to 2024, youth migration charts a cycle of ambition and return, a pattern as familiar as it is frustrating. For Tasmania’s young people, the mainland isn’t just a place of opportunity, it’s the only horizon that seems to move. The 20–24 age group drives this flow, leaving for study, early careers, and the feeling of progress. When borders closed, that outward stream reversed briefly, a pause forced by circumstance rather than change. As soon as movement became possible again, the pattern snapped back—sharper, faster, and more revealing than before. The data traces that motion like a heartbeat: each dip and rise a reminder that talent leaves easily, and rarely comes home for long.
Figure.2 Movement peaks for ages 20–24 — the study-and-first-job window. The pattern flips briefly in 2021, then resumes.
Figure.3 When net migration dips for all Tasmanians, it is typically youth who fall furthest — amplifying total change.
Figure.4 Over time, small yearly losses compound — a slow, steady youth exodus that shapes Tasmania’s future workforce.
| Metric | Result |
|---|---|
| Total records | 32 |
| Missing values | 0 |
| Minimum value | -4410 |
| Maximum value | 3050 |
Interpretation.
Zero missing values and consistent numeric
ranges across 2017–2024 confirm the dataset’s reliability.
Values
reflect ABS rounding to the nearest 10 persons.
REFERENCES
Australian Bureau of Statistics. (1997–2024).
Interstate migration: Arrivals, departures and net, State/territory,
Age and sex — Calendar years (NIM_CY) [Data set]. ABS.
https://www.abs.gov.au/
Wickham, H., et al. (2023). ggplot2: Elegant Graphics for Data
Analysis. Springer.<
---
title: "Leaving Home: The Drift of Tasmania’s Youth"
author: "Thomas Dobson <br> s4096773"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
theme: cosmo
source_code: embed
---
```{r setup, message=FALSE, warning=FALSE}
library(readr)
library(dplyr)
library(ggplot2)
library(plotly)
library(scales)
library(flexdashboard)
library(tidyr)
library(kableExtra)
nim <- read_csv("NIM_Tasmania_Youth_2017-2024.csv", show_col_types = FALSE)
# Handle header variants from ABS
if (all(c("TIME_PERIOD","OBS_VALUE","Measure") %in% names(nim))) {
nim <- nim %>% rename(Year = TIME_PERIOD, Value = OBS_VALUE)
} else if (all(c("Time Period","Observation Value","Measure") %in% names(nim))) {
nim <- nim %>% rename(Year = `Time Period`, Value = `Observation Value`)
} else stop("Unexpected column names in dataset.")
nim <- nim %>%
filter(Measure=="Net Interstate Migration", Region=="Tasmania") %>%
mutate(Year = as.integer(Year))
youth_groups <- c("15-19","20-24","25-29")
nim_youth <- nim %>% filter(Age %in% youth_groups)
nim_total <- nim %>% filter(Age == "All ages")
youth_sum <- nim %>%
filter(Age %in% youth_groups) %>%
group_by(Year) %>% summarise(Value = sum(Value, na.rm=TRUE), .groups="drop")
total_sum <- nim_total %>% select(Year, Value)
year_share <- youth_sum %>%
rename(Youth = Value) %>%
inner_join(total_sum %>% rename(Total = Value), by="Year") %>%
mutate(YouthShare = 100 * Youth / if_else(Total==0, NA_real_, Total))
cum_df <- bind_rows(
youth_sum %>% mutate(Group="Youth 15–29"),
total_sum %>% mutate(Group="All ages")
) %>%
arrange(Group, Year) %>%
group_by(Group) %>%
mutate(Cumulative = cumsum(replace_na(Value, 0))) %>%
ungroup()
#colours defined
col_panel <- "white"
col_grid <- "black"
col_text <- "black"
col_accent1 <- "#00B2A9" # teal
col_accent2 <- "#FFC857" # amber
col_accent3 <- "#EF476F" # rose
theme_cinema <- theme_minimal(base_size = 15) +
theme(
text = element_text(colour = col_text),
plot.title = element_text(face="bold", size=18, colour=col_text),
plot.subtitle = element_text(size=13, colour="black", margin=margin(b=8)),
axis.text = element_text(colour=col_text),
axis.title = element_text(colour=col_text),
legend.text = element_text(colour=col_text),
legend.title = element_blank(),
panel.grid.minor = element_blank(),
panel.grid.major = element_line(colour = col_grid),
panel.grid.major.x = element_line(colour = col_grid),
plot.background = element_rect(fill = col_panel, colour = NA),
panel.background = element_rect(fill = col_panel, colour = NA),
legend.background = element_rect(fill = col_panel, colour = NA)
)
# Helper to apply Plotly dark surface consistently
dark_plotly <- function(p) {
ggplotly(p, tooltip = "text") %>%
layout(
paper_bgcolor = col_panel,
plot_bgcolor = col_panel,
font = list(color = col_text),
legend = list(orientation="h", x=0, y=-0.15)
) %>%
config(displayModeBar = TRUE, displaylogo = FALSE)
}
```
```{css, echo=FALSE}
/* Cinematic dark chrome */
body { background-color: #0B1B2B; color: black; overflow: hidden; }
.navbar { background-color: #071322 !important; border-bottom: 1px solid #203245; }
.navbar-brand, .navbar-nav > li > a { color: #E8EFF6 !important; font-weight: 600; }
.section.level2 h3 { margin-top: 6px; margin-bottom: 6px; color: #E8EFF6; }
hr { border-top: 1px solid #203245; }
/* Cards */
.card-dark { background:#0F1A27; border:1px solid #203245; border-radius:10px; padding:10px 14px; }
.caption { color:black; font-size: 0.95rem; margin-top:6px; }
/* Value boxes tuned for dark theme */
.value-box .icon { opacity: 0.9; }
.value-box .caption { color:black !important; }
.value-box .value { color:black !important; }
/* Tighten top narrative row spacing */
.narr { font-style: italic; color:#CFE3F3; line-height:1.4; margin:6px 0 0 0; }
```
# 1. Leaving the Island {data-icon="fa-plane-departure"}
Row {data-height=28}
-----------------------------------------------------------------------
<div class="card-dark narr">
From 2017 to 2024, youth migration traces a rhythm of hope, disruption, and return. For many young Tasmanians, leaving isn’t rebellion, it’s survival. The 20–24 group drives that story: chasing study, work, and momentum the island rarely offers. When borders closed, home drew them back; but as soon as the world reopened, the familiar pull outward resumed. The data shows what many of us already feel that opportunity still lives elsewhere, and Tasmania keeps losing its future one departure at a time.
</div>
Row {data-height=72}
-----------------------------------------------------------------------
### Youth share of total migration, 2017–2024
```{r}
p_share <- ggplot(
year_share,
aes(Year, YouthShare,
text = paste0("Year: ", Year,
"<br>Youth share: ", round(YouthShare,1), "%"))
) +
geom_col(fill = col_accent1, width = 0.65) +
geom_text(aes(label = ifelse(is.na(YouthShare), "", paste0(round(YouthShare,1),"%"))),
vjust = -0.35, colour = col_text, size = 4.1) +
geom_vline(xintercept = 2021, linetype="dashed", colour="#8AA7C1") +
annotate("text", x=2021, y = max(year_share$YouthShare, na.rm=TRUE) + 8,
label="COVID rebound", colour="#8AA7C1", fontface="italic") +
labs(
title = "Younger Tasmanians form a shifting share of total net migration",
subtitle = "A brief pandemic reversal — then the familiar outward pull returns",
x = "Year", y = "% of total migration", colour="black"
) + theme_cinema
dark_plotly(p_share) %>% layout(margin=list(t=60, b=40))
```
> <span class="caption">**Figure.1** Bars show the percentage of Tasmania’s total net interstate migration accounted for by ages 15–29.
> 2021 rises as borders close and students/workers return; after reopening, youth again make up a large share of the outward flow.</span>
# 2. The Drift Over Time {data-icon="fa-chart-line"}
Row {data-height=15}
-----------------------------------------------------------------------
<div class="card-dark narr">
From 2017 to 2024, youth migration charts a cycle of ambition and return, a pattern as familiar as it is frustrating. For Tasmania’s young people, the mainland isn’t just a place of opportunity, it’s the only horizon that seems to move. The 20–24 age group drives this flow, leaving for study, early careers, and the feeling of progress. When borders closed, that outward stream reversed briefly, a pause forced by circumstance rather than change. As soon as movement became possible again, the pattern snapped back—sharper, faster, and more revealing than before. The data traces that motion like a heartbeat: each dip and rise a reminder that talent leaves easily, and rarely comes home for long.
</div>
Row {data-height=85}
-----------------------------------------------------------------------
Column {data-width=50}
-----------------------------------------------------------------------
### By age group: the strongest flows are in study years
```{r}
p_youth <- ggplot(
nim_youth,
aes(Year, Value, color = Age, group = Age,
text = paste0("Year: ", Year, "<br>Age: ", Age,
"<br>Net migration: ", comma(Value)))
) +
geom_line(linewidth = 1.4) +
geom_point(size = 3) +
scale_color_brewer(palette = "Dark2") +
labs(
title = "Youth migration by age group (2017–2024)",
subtitle = "20–24 is consistently the most mobile cohort",
x = "Year", y = "Net migration (persons)"
) + theme_cinema +
theme(
plot.title = element_text(size = 12, face = "bold"),
plot.subtitle = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 9),
legend.text = element_text(size = 9)
)
dark_plotly(p_youth) %>% layout(margin=list(t=60, b=40))
```
> <span class="caption">**Figure.2** Movement peaks for ages **20–24** — the study-and-first-job window. The pattern flips briefly in 2021, then resumes.</span>
### Youth vs total: same direction, sharper swings
```{r}
compare <- bind_rows(
youth_sum %>% mutate(Group="Youth 15–29"),
total_sum %>% mutate(Group="All ages")
) %>% mutate(Tip = paste0(Group, "<br>Year: ", Year, "<br>Net migration: ", comma(Value)))
p_compare <- ggplot(
compare, aes(Year, Value, color = Group, group = Group, text = Tip)
) +
geom_line(linewidth = 1.4) +
geom_point(size = 3) +
scale_color_manual(values = c("Youth 15–29" = col_accent1, "All ages" = col_accent2)) +
labs(
title = "Youth vs total migration — Tasmania",
subtitle = "Youth flows mirror the total trend but with greater volatility",
x = "Year", y = "Net migration (persons)"
) + theme_cinema +
theme(
plot.title = element_text(size = 12, face = "bold"),
plot.subtitle = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 9),
legend.text = element_text(size = 9)
)
dark_plotly(p_compare) %>% layout(margin=list(t=60, b=40))
```
> <span class="caption">**Figure.3** When net migration dips for all Tasmanians, it is typically **youth who fall furthest** — amplifying total change.</span>
Column {data-width=50}
-----------------------------------------------------------------------
### Cumulative net migration since 2017
```{r}
p_cum <- ggplot(
cum_df,
aes(Year, Cumulative, color = Group, group = Group,
text = paste0("Year: ", Year, "<br>", Group,
"<br>Cumulative since 2017: ", comma(Cumulative)))
) +
geom_line(linewidth = 1.6) +
geom_point(size = 3) +
scale_color_manual(values = c("Youth 15–29" = col_accent1, "All ages" = col_accent2)) +
labs(
title = "Long-run drift",
subtitle = "Accumulated net change over the period",
x = "Year", y = "Cumulative net migration (persons)"
) + theme_cinema +
theme(
plot.title = element_text(size = 12, face = "bold"),
plot.subtitle = element_text(size = 10),
axis.title = element_text(size = 10),
axis.text = element_text(size = 9),
legend.text = element_text(size = 9)
)
dark_plotly(p_cum) %>% layout(margin=list(t=60, b=40))
```
> <span class="caption">**Figure.4** Over time, small yearly losses **compound** — a slow, steady youth exodus that shapes Tasmania’s future workforce.</span>
# 3. Coming Home? {data-icon="fa-home"}
Row {data-height=100}
-----------------------------------------------------------------------
<div style="width:100vw; display:flex; justify-content:center;">
<div style="display:flex; flex-direction:column; align-items:center; justify-content:center; gap:22px; min-height:85vh; padding-top:40px; padding-bottom:40px; width:70%; max-width:900px;">
<!-- Validation summary -->
<div style="background-color:#ffffff; color:#000000; padding:16px; border-radius:6px; box-shadow:0 0 6px rgba(0,0,0,0.25); width:100%; text-align:left; font-size:13px;">
```{r, echo=FALSE, results='asis'}
data_check <- tibble(
Metric = c("Total records", "Missing values", "Minimum value", "Maximum value"),
Result = c(nrow(nim), sum(is.na(nim$Value)),
min(nim$Value, na.rm = TRUE), max(nim$Value, na.rm = TRUE))
)
knitr::kable(
data_check,
caption = "Validation summary",
align = "lc"
) |>
kableExtra::kable_styling(full_width = TRUE, position = "center") |>
kableExtra::row_spec(0, bold = TRUE, color = "black", background = "white") |>
kableExtra::row_spec(1:nrow(data_check), color = "black", background = "white") |>
kableExtra::column_spec(1:2, border_left = TRUE, border_right = TRUE)
```
<p style="margin-top:10px;">
<b>Interpretation.</b><br>
Zero missing values and consistent numeric ranges across 2017–2024 confirm the dataset’s reliability.<br>
Values reflect ABS rounding to the nearest 10 persons.
</p>
</div>
<!-- Conclusion -->
<div style="background-color:#ffffff; color:#000000; padding:14px; border-radius:6px; box-shadow:0 0 6px rgba(0,0,0,0.25); width:100%; line-height:1.5; text-align:left; font-size:13px;">
<ul style="margin-top:0; padding-left:18px;">
CONCLUSION<br>
<li>Tasmania’s migration story is about <strong>movement and renewal</strong>, not decline.</li>
<li>The outward flow of youth reflects ambition and the pull of opportunity beyond the island.</li>
<li>The goal is not to stop that flow, but to <strong>reshape the return journey</strong> — making homecoming a real and rewarding choice.</li>
<li>A stronger Tasmania builds <strong>sustainable jobs</strong>, <strong>creative industries</strong>, and <strong>affordable housing</strong> that turn experience gained elsewhere into growth at home.</li>
<li>Migration becomes renewal: talent leaving, learning, and returning with new energy.</li>
<li>The aim isn’t to anchor youth in place, but to ensure that <strong>home always remains within their orbit.</strong></li>
</ul>
</div>
<!-- References -->
<div style="background-color:#ffffff; color:#000000; padding:8px; border-radius:6px; box-shadow:0 0 6px rgba(0,0,0,0.25); width:100%; text-align:left; font-size:11px; line-height:1.4;">
<div>
REFERENCES<br>
Australian Bureau of Statistics. (1997–2024). <em>Interstate migration: Arrivals, departures and net, State/territory, Age and sex — Calendar years (NIM_CY)</em> [Data set]. ABS. <a href="https://www.abs.gov.au/" style="color:#000000;">https://www.abs.gov.au/</a><br><br>
Wickham, H., et al. (2023). <em>ggplot2: Elegant Graphics for Data Analysis</em>. Springer.<