AstroPaper v6.1.0 shipped on July 7, 2026. I shipped my own Astro 7 upgrade on July 10. We’d been working in parallel without knowing it: their PR #664 was already merged by the time I started digging into the framework’s new defaults. The first thing I did after merging mine was check what they’d done differently.
This post is also a working demo. There are five callouts in this file, two ResponsiveTables, and the whole thing is .mdx. If you see a colored admonition block or a striped table, that’s the new pipeline rendering.
This is how I sync my fork without losing my mind.
Drift has two speeds
Once you fork a project, drift is inevitable. The question is what kind.
Compounding drift. Dep patches you skip stack with future versions. If you stay on ESLint 9 when upstream moves to 10, the next upgrade hits you twice: once for the version you’re on, once for the version upstream pinned. Always sync these.
Non-compounding drift. Structural rewrites don’t stack. Skip them unless you actually want the underlying feature. AstroPaper v6.0.0 was a major rewrite: i18n scaffold, config-as-function, named exports. None of it applies to my fork. Skipping it doesn’t make future syncs harder. Skipping it doesn’t make them easier either; it’s just one less thing to merge.
The heuristic: if the change is “bump version X to version Y” it’s compounding. If it’s “rewrite module Z to do the same thing differently” it’s not. The first is a debt that grows. The second is a one-shot refactor that stays the same size whether you take it now or later.
What I took from upstream v6.1.0
Their Astro 7 upgrade was PR #664. Five files changed. Mostly dep bumps.
| Package | Upstream v6.1.0 | My fork before | My fork after |
|---|---|---|---|
eslint | 10.6.0 | 9.39.4 | 10.6.0 |
eslint-plugin-astro | 2.1.1 | 1.7.0 | 2.1.1 |
sharp | 0.35.2 | 0.34.5 | 0.35.2 |
prettier | 3.9.3 | 3.8.4 | 3.9.3 |
@shikijs/transformers | 4.3.0 | 3.23.0 | 4.3.0 |
tailwindcss | 4.3.2 | 4.3.1 | 4.3.2 |
Total: 30 minutes including bun install and one Prettier reformat commit. The interesting one is eslint-plugin-astro 1.7 → 2.1: upstream labels it “required for Astro v7 compat,” which I missed in my own Astro 7 upgrade. So that’s a real win. Upstream bumped it because the plugin’s internals needed Astro 7 hooks; my fork stayed on 1.7 because nothing errored. Would have bitten me the next time someone reported a lint false positive.
What I left on the table
Don’t take a structural rewrite just because it’s there. AstroPaper v6.0.0’s i18n scaffold is genuinely well-built. But if you don’t ship multiple locales, all you’re importing is a config change you’ll have to maintain yourself.
Upstream v6.0.0 was a big rewrite on top of the Astro 7 bump. I left almost all of it.
- i18n scaffold. Multi-locale routing. I have Indonesian content but it’s mixed in with English. Adding
/id/URLs is a separate project, not a sync. - Config-as-function. Upstream moved
astro.config.tsto import from a separateastro-paper.config.tsfunction. No functional benefit for me. - Named-export
getSortedPosts. Their default export became a named export. I use default; my codebase works. - Breadcrumb refactor. They moved from
<ul>to flex-per-<li>withgap-x-1. I fixed mine differently:<ol>+gap-x-2. Same effect.
These don’t make future syncs harder. They just mean future syncs ignore them.
The features worth stealing
Even when you skip a rewrite, individual features often make sense on their own. I pulled three from upstream this round.
Take features, not rewrites. A standalone feature like <ResponsiveTable> is one component file you copy in. A rewrite is 60+ files of new structure. Different cost.
Callouts (PR #655). The rehype-callouts plugin converts > [!WARNING], > [!TIP], > [!NOTE] blockquotes into styled admonition boxes. I was already using ad-hoc blockquotes in posts; this standardizes them. Add the dep, configure the plugin, import the stylesheet, sync the theme .dark class. Done. Five callouts in this post alone, you can see them above and below.
ResponsiveTable (PR #664). A wrapper component for <table> that handles narrow viewports and zebra striping. Wraps any existing markdown pipe table with the same data. Variants: default, minimal, striped, striped-minimal. Compare the table above (striped) to the ones in my OpenClaw post where I used both striped and striped-minimal for different visual weight.
MDX. Adding @astrojs/mdx was a side-effect of wanting to use ResponsiveTable in posts. Markdown posts can’t import Astro components inline. .mdx posts can. The cost is small: one integration, one glob pattern extension, and it doesn’t break existing .md posts. I’m converting posts to .mdx only when they actually benefit from a component; everything else stays .md.
MDX has one footgun: text that looks like JSX gets parsed as JSX. i < 5 and Foo style are the usual offenders. Escape with \< or wrap in backticks. Posts that talk about HTML or React components hit this most often.
What I’m not taking yet
A few things I left for later:
- Image lightbox (PR #654). Click any image in a post → fullscreen overlay. Touches
src/pages/posts/[...slug]/index.astro, which is my most customized file. Some merge work needed. - Sanitize post title for view-transition-name (PR #650). One-line fix for non-ASCII titles. Trivial cost; just haven’t gotten to it.
- Better slugify (PR #606). Upstream improved acronym handling. Affects my Indonesian posts. Low risk but needs an audit of existing slug dependencies.
These go on the next sync. None of them are urgent.
The playbook
| Step | What to do |
|---|---|
| Clone shallow | git clone —depth=50 upstream-url |
| Map overlap | git diff —stat their-tag..their-main against files in your fork |
| Separate compounding vs non-compounding | Dep patches vs structural rewrites |
| Sync compounding | Branch + bump + verify |
| Skip non-compounding | Document the skip so future-you remembers |
| Steal features individually | Copy the file, port the integration, keep your own structure |
Step one is the most important: knowing what overlap you actually have. A shallow clone of upstream + a git diff --stat against your fork’s tree tells you the conflict surface. Anything in the diff that doesn’t touch a file you’ve modified is upstream-only and you can ignore it.
Drift isn’t a problem to solve. It’s a process to manage. Pick the features that match your fork’s direction. Sync the patches that prevent compounding. Skip the rest.
Your fork doesn’t have to be a clean merge target for upstream to be valuable.
Working in parallel with upstream wasn’t planned. The sync turned out cleaner than it had any right to.