Migrating from v4 to v5
This page explains how to migrate from minista v4 to v5.
If you use v3 or earlier, first see the v3 to v4 migration guide (Japanese).
Table of Contents
- Migrate with agents
- Requirements and setup
- pluginMdx and pluginBundle integration into SSG
- pluginEntry integration into SSG
- HTML language and layout
- Beautify output settings
- Moving image preload settings
- HTML assets
- SVG attributes and missing files
- SVG and sprite IDs
- Archive source and missing files
- Island lazy loading
- Verify migration
Migrate with agents
Give an agent this page's URL and ask it to migrate from v4 to v5. Along with the URL, specify the target project and what to verify. With the project open, you can use a request like this:
Migrate this project from minista v4 to v5.
Read the following migration guide and update dependencies, configuration, and code.
https://minista.dev/docs/reference/migration
Preserve appearance and functionality, then check types, build, and preview after migration.
Report the changes and any items you could not verify.After updating to v5, run npx minista agents to retrieve the agent guide for the installed version. See Building with agents for using agents.
Requirements and setup
Node.js ^20.19.0 || >=22.12.0 and Vite ^8.1.0 are required.
React and React DOM peerDependencies changed to >=19.0.0. React 18 support and dedicated tests have ended; React 19 is the verification baseline. Update both packages to version 19 or later when migrating projects using React 18.
If you use TypeScript, update @types/react and @types/react-dom to match your React version.
Basic Vite configuration remains usable. Move the public MDX, Bundle, and Entry plugin APIs into SSG as described below. Internally, builds changed from launching the Vite CLI twice for HTML generation and browser output to building both environments in sequence within one Vite application build.
After moving settings for plugins integrated into SSG, remove any remaining --oneBuild from commands. Using this option in v5 exits with MINISTA_CLI_OPTION_REMOVED.
- minista build --oneBuild
+ minista buildv5 provides the following commands and machine-readable output.
minista check [--json]: Check routes, pages, andgetStaticData()minista inspect [--json]: Show the Project Graph from source filesminista inspect --manifest [--json]: Show only the built manifestminista explain <node-id> [--json]: Explain Graph node relationships
The working directory is node_modules/.minista if the root has package.json, or .minista otherwise. Data stored directly under the root by older versions is not automatically moved, deleted, or read. Regenerate it with check or a build after updating. Use minista agents --json to retrieve storage locations and minista agents --write to add AI agent instructions to an existing project.
node_modules/.minista/manifest.json: Build-time project structure and output informationnode_modules/.minista/diagnostics.json: Diagnostics from the latest check or build
Regular CLI usage does not require building minista itself beforehand.
pluginMdx and pluginBundle integration into SSG
Remove pluginMdx() and pluginBundle() from imports and the plugins array, and move their settings to mdx and bundle in pluginSsg() respectively.
import { defineConfig, pluginSsg } from "minista"
export default defineConfig({
plugins: [
pluginSsg({
mdx: { frontmatter: { name: "metadata" } },
bundle: { outName: "bundle" },
}),
],
})MDX and Markdown are enabled by default; set mdx: false if unneeded. Bundle's separate src was removed, so consolidate page discovery in pluginSsg({ src: [...] }). useExportCss was also removed, and CSS imported by pages and Layout is always output. CSS Modules use the same transformation results as the HTML generation environment.
See pluginSsg for details.
pluginEntry integration into SSG
pluginEntry() was removed in v5. pluginSsg() alone builds CSS, JavaScript, and image references from HTML. Remove pluginEntry from imports and the plugins array. Existing references and Vite output settings remain usable, including alongside public CSS and JS.
import { defineConfig, pluginSsg } from "minista"
export default defineConfig({ plugins: [pluginSsg()] })Cases where a script URL was incorrectly replaced with CSS imported by JS, and public CSS was inserted twice in compatibility builds, were also fixed.
pluginSsg() now returns an array of internal Vite plugins. The usual plugins: [pluginSsg()] needs no change. If custom code directly inspects the return value, flatten the array before inspecting individual plugins.
HTML language and layout
The default html language changed from ja to en. Explicitly set lang="ja" for Japanese sites.
In v5, write html, head, and body in Layout to edit the entire HTML structure and attributes. The following layout is output directly as the page's HTML.
import type { LayoutProps } from "minista/types"
export default function Layout({ title, children }: LayoutProps) {
return (
<html lang="ja">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>{title}</title>
</head>
<body>{children}</body>
</html>
)
}For layouts that do not write the entire HTML document, continue using Head's htmlAttributes as before.
import { Head } from "minista"
import type { LayoutProps } from "minista/types"
export default function Layout({ children }: LayoutProps) {
return (
<>
<Head htmlAttributes={{ lang: "ja" }} />
<main>{children}</main>
</>
)
}See Layouts for structural changes and Head for per-page titles and meta elements.
Beautify output settings
In v4, pluginBeautify() changed JS and CSS content even after file name hashes were assigned. The hash could therefore differ from the final content. v5 revises formatting timing and reports errors when output settings are incompatible with formatting.
To format JS and CSS along with HTML, configure them as follows.
import { defineConfig, pluginSsg, pluginBeautify } from "minista"
export default defineConfig({
plugins: [pluginSsg(), pluginBeautify()],
build: {
sourcemap: false,
cssMinify: false,
rolldownOptions: {
output: {
minify: false,
assetFileNames: "assets/[name][extname]",
},
},
},
})- JS is formatted before hashing.
build.rolldownOptions.output.minify: falseis required to prevent minification after formatting.build.minify: falsealone does not disable all Rolldown processing. JSentryFileNamesandchunkFileNamescan still use[hash]. - CSS is formatted after file names are finalized. Set
assetFileNamesto a string without[hash]to avoid mismatches between hashes and content. This setting also applies to assets such as images. SetcssMinify: falseto disable CSS minification as well. - Disable source maps when formatting JS or CSS because maps matching the formatted positions cannot be generated.
To format only HTML, narrow the targets; no changes to JS or CSS output settings are needed.
import { defineConfig, pluginSsg, pluginBeautify } from "minista"
export default defineConfig({
plugins: [pluginSsg(), pluginBeautify({ src: ["**/*.html"] })],
})See pluginBeautify for settings and diagnostics.
Moving image preload settings
Move removeImagePreload from pluginBeautify to pluginSsg. Explicit use of the old option produces MINISTA_BEAUTIFY_OPTION_MOVED.
import { defineConfig, pluginSsg } from "minista"
export default defineConfig({
plugins: [pluginSsg({ removeImagePreload: false })],
})SSG defaults to true. Even projects that did not use Beautify now remove image preloads from React renderer output in both development and builds. Set false as above to retain them. The target changed from direct body children to all renderer output, so move image preloads written directly in Layout's head to the Head API to retain them. Controlling preloads through Beautify target globs is no longer supported.
HTML assets
The Entry feature integrated into SSG now rewrites the same targets it collects: link[href], script[src], img[src], img[srcset], source[srcset], and use[href]. Excluded attributes such as meta[content], video[poster], and a[href] are unchanged even when their value matches a URL collected elsewhere. Use a URL from an SSG module import or a public asset URL to reference generated assets in these attributes.
CSS imported by an entry is inserted once, only into referencing pages. If you relied on implicit CSS from another page's entry, import CSS from the target page or Layout, or add an explicit stylesheet link. External URLs starting with // are not collected, and a base with an absolute CDN URL retains its protocol. Queries and fragments are preserved but not interpreted as Vite module transformation queries. See HTML assets in pluginSsg for details.
SVG attributes and missing files
pluginSvg() preserves drawing-related root attributes after SVGO optimization. Values such as fill, stroke, and dimensions in the source file appear in output. Override them with props of the same name on Svg. style and className replace entire attributes.
Missing source files are no longer silently left as references; they fail the build with MINISTA_SVG_SOURCE_NOT_FOUND. src resolves relative to the project root. Source changes during development are reflected without restarting.
SVG and sprite IDs
Internal IDs now have deterministic prefixes. Avoid external CSS or JavaScript references to original IDs; use classes or explicit props. Sprite public symbol IDs are preserved, but duplicates cause MINISTA_SPRITE_DUPLICATE_SYMBOL instead of overwriting. Rename conflicting symbols. Sprite also now preserves original root drawing attributes and shared defs.
Archive source and missing files
When pluginArchive() or archives[].srcDir is omitted, the resolved build.outDir is used as input instead of a fixed dist. For example, build.outDir: "output" compresses output and defaults to output/dist.zip. Explicitly set srcDir: "dist" to keep using a separate dist as before.
Missing input now causes MINISTA_ARCHIVE_SOURCE_NOT_FOUND instead of successfully creating an empty archive. Input that is not a directory causes MINISTA_ARCHIVE_SOURCE_NOT_DIRECTORY. Existing empty directories remain allowed. All output paths configured in the same archives, including previous output, are excluded from input to prevent nested archives on rebuild.
Island lazy loading
client:visible, client:media, and client:idle now defer component JavaScript fetching and module evaluation as well as hydration until their condition is met. If you used top-level side effects in component modules to initialize pages, move them to an immediately executed script. client:load and client:only begin fetching when the Island entry runs. SSG CSS for initial display is preserved.
Verify migration
After updating configuration and dependencies, check in the following order.
npx minista check --json
npx minista inspect --json
npx tsc --noEmit
npm run build
npm run previewProjects without TypeScript do not need tsc. Check generated HTML, CSS, images, Island interactions, and delivery archives. If fetching CMS data, also provide environment variables required for checks and builds.
See CLI for command details and the configuration reference for output settings.