chore: the blog (#5453)
* feat: start on blogpost * feat: images for post * fix: grammar + prerender news + changelog * feat: add discovery in app vid * fix lint * rename new blog md to match title * fix assets directories * remove left over compiled files * update thumbnail --------- Co-authored-by: tdgao <mr.trumgao@gmail.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com>
12
AGENTS.md
@@ -1,13 +1 @@
|
||||
See [CLAUDE.md](./CLAUDE.md) for all project instructions and guidelines.
|
||||
|
||||
## Skills
|
||||
|
||||
Project-specific skills (patterns, conventions, and implementation guides) are located in [`.claude/skills/`](./.claude/skills/). Each skill has a `SKILL.md` describing the pattern:
|
||||
|
||||
- **[Dependency Injection](./.claude/skills/dependency-injection/SKILL.md)** — Vue provide/inject DI layer using `createContext`
|
||||
- **[Cross-Platform Pages](./.claude/skills/cross-platform-pages/SKILL.md)** — Shared component architecture across Nuxt and Tauri frontends
|
||||
- **[Multistage Modals](./.claude/skills/multistage-modals/SKILL.md)** — Wizard-like modal flows with `MultiStageModal`
|
||||
- **[Figma MCP](./.claude/skills/figma-mcp/SKILL.md)** — Translating Figma designs to Modrinth Vue components
|
||||
- **[i18n Convert](./.claude/skills/i18n-convert/SKILL.md)** — Converting hard-coded strings to vue-i18n localization
|
||||
- **[API Module](./.claude/skills/api-module/SKILL.md)** — Adding new endpoint modules to `@modrinth/api-client`
|
||||
- **[TanStack Query](./.claude/skills/tanstack-query/SKILL.md)** — Server state management with `@tanstack/vue-query` v5
|
||||
|
||||
12
CLAUDE.md
@@ -84,3 +84,15 @@ Each project may have its own `CLAUDE.md` with detailed instructions:
|
||||
|
||||
### General
|
||||
- Do not create new non-source code files (e.g. Bash scripts, SQL scripts) unless explicitly prompted to
|
||||
|
||||
## Skills
|
||||
|
||||
Project-specific skills (patterns, conventions, and implementation guides) are located in [`.claude/skills/`](./.claude/skills/). Each skill has a `SKILL.md` describing the pattern:
|
||||
|
||||
- **[Dependency Injection](./.claude/skills/dependency-injection/SKILL.md)** — Vue provide/inject DI layer using `createContext`
|
||||
- **[Cross-Platform Pages](./.claude/skills/cross-platform-pages/SKILL.md)** — Shared component architecture across Nuxt and Tauri frontends
|
||||
- **[Multistage Modals](./.claude/skills/multistage-modals/SKILL.md)** — Wizard-like modal flows with `MultiStageModal`
|
||||
- **[Figma MCP](./.claude/skills/figma-mcp/SKILL.md)** — Translating Figma designs to Modrinth Vue components
|
||||
- **[i18n Convert](./.claude/skills/i18n-convert/SKILL.md)** — Converting hard-coded strings to vue-i18n localization
|
||||
- **[API Module](./.claude/skills/api-module/SKILL.md)** — Adding new endpoint modules to `@modrinth/api-client`
|
||||
- **[TanStack Query](./.claude/skills/tanstack-query/SKILL.md)** — Server state management with `@tanstack/vue-query` v5
|
||||
|
||||
@@ -111,6 +111,8 @@ export default defineNuxtConfig({
|
||||
const docTemplates = Object.keys(
|
||||
await import('./src/templates/docs/index.ts').then((m) => m.default),
|
||||
)
|
||||
const blogArticles = await import('@modrinth/blog').then((m) => m.articles)
|
||||
const { getChangelog } = await import('@modrinth/utils')
|
||||
|
||||
nitroConfig.prerender = nitroConfig.prerender || {}
|
||||
nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
|
||||
@@ -120,6 +122,15 @@ export default defineNuxtConfig({
|
||||
for (const template of docTemplates) {
|
||||
nitroConfig.prerender.routes.push(`/_internal/templates/doc/${template}`)
|
||||
}
|
||||
nitroConfig.prerender.routes.push('/news')
|
||||
for (const article of blogArticles) {
|
||||
nitroConfig.prerender.routes.push(`/news/article/${article.slug}`)
|
||||
}
|
||||
nitroConfig.prerender.routes.push('/news/changelog')
|
||||
for (const entry of getChangelog()) {
|
||||
const id = entry.version ?? entry.date.unix()
|
||||
nitroConfig.prerender.routes.push(`/news/changelog/${entry.product}/${id}`)
|
||||
}
|
||||
},
|
||||
async 'build:before'() {
|
||||
// 30 minutes
|
||||
|
||||
@@ -9,13 +9,9 @@ const changelogEntry = computed(() =>
|
||||
route.params.date
|
||||
? getChangelog().find((x) => {
|
||||
if (x.product === route.params.product) {
|
||||
console.log('Found matching product!')
|
||||
|
||||
if (x.version && x.version === route.params.date) {
|
||||
console.log('Found matching version!')
|
||||
return x
|
||||
} else if (x.date.unix() === Number(route.params.date as string)) {
|
||||
console.log('Found matching date!')
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 115 KiB |
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"articles": [
|
||||
{
|
||||
"title": "Introducing Server Projects",
|
||||
"summary": "A new project type made for seamless modded multiplayer on Modrinth.",
|
||||
"thumbnail": "https://modrinth.com/news/article/introducing-server-projects/thumbnail.webp",
|
||||
"date": "2026-03-02T15:00:00.000Z",
|
||||
"link": "https://modrinth.com/news/article/introducing-server-projects"
|
||||
},
|
||||
{
|
||||
"title": "Streamlined Version Creation",
|
||||
"summary": "Version creation is now dramatically more intelligent and easier for creators.",
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
import type { FunctionalComponent, SVGAttributes } from 'vue'
|
||||
|
||||
export type IconComponent = FunctionalComponent<SVGAttributes>
|
||||
|
||||
import _AffiliateIcon from './icons/affiliate.svg?component'
|
||||
import _AlignLeftIcon from './icons/align-left.svg?component'
|
||||
import _ArchiveIcon from './icons/archive.svg?component'
|
||||
@@ -378,8 +380,6 @@ import _XCircleIcon from './icons/x-circle.svg?component'
|
||||
import _ZoomInIcon from './icons/zoom-in.svg?component'
|
||||
import _ZoomOutIcon from './icons/zoom-out.svg?component'
|
||||
|
||||
export type IconComponent = FunctionalComponent<SVGAttributes>
|
||||
|
||||
export const AffiliateIcon = _AffiliateIcon
|
||||
export const AlignLeftIcon = _AlignLeftIcon
|
||||
export const ArchiveIcon = _ArchiveIcon
|
||||
|
||||
124
packages/blog/articles/introducing-server-projects.md
Normal file
@@ -0,0 +1,124 @@
|
||||
---
|
||||
title: Introducing Server Projects
|
||||
summary: A new project type made for seamless modded multiplayer on Modrinth.
|
||||
date: 2026-03-02T15:00:00+00:00
|
||||
authors: ['AJfd8YH6', '6EjnV9Uf', 'xSQqYYIN']
|
||||
---
|
||||
|
||||
Big news: we’re shipping our first new project type in years!
|
||||
|
||||
Server Projects are coming to Modrinth, and they’re not just a typical server list. They’re deeply integrated into the platform and app in ways that make playing seamless.
|
||||
|
||||
We genuinely believe modded is the future of large-scale multiplayer Minecraft, so let's jump in!
|
||||
|
||||
<div class="video-wrapper mb-8">
|
||||
<video autoplay loop muted playsinline>
|
||||
<source src="./server-discovery-in-app.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
### TL;DR
|
||||
|
||||
- New Server Project type
|
||||
- Three compatibility types: vanilla, modded (published pack), or modded (uploaded pack)
|
||||
- Joining a server from the app downloads the required content and launches you directly into the server
|
||||
- New linked server instance type that receives updates from the server
|
||||
- Server Projects are not eligible for payouts
|
||||
|
||||
## Design Goals
|
||||
|
||||
Let’s start with why we built this.
|
||||
|
||||
Most of the fun in Minecraft happens with other people, but getting into a modded multiplayer experience is still harder than it should be. We think this is the future of multiplayer for a few reasons:
|
||||
|
||||
- Modding enables far deeper experiences than server-side plugins ever will.
|
||||
- The real constraint has been distribution and setup. Players have to find the server, install the right content, keep it updated, and hope everything matches.
|
||||
- Multiplayer discovery in Minecraft has never been great. It should be easier to join a server, and just as easy to join a modded one.
|
||||
|
||||
Additionally, modpack discovery has become noisy on Modrinth. Servers fork popular packs with small tweaks and climb discovery, which crowds out original creators.
|
||||
|
||||
## Project Creation
|
||||
|
||||
Server Projects are different from other project types because they don’t always have uploaded files. Instead, servers define their compatibility, which can include specifying any required content.
|
||||
|
||||
For the initial release, we support two compatibility models: vanilla and required modpack. We also have ideas to expand this with a minimum requirements model in the future. Authors would define the required mod and version needed to join and could recommend modpacks that work as well.
|
||||
|
||||
When setting up your server project, you define this in the Server Compatibility section. It comes in three types:
|
||||
|
||||
- Vanilla server
|
||||
- Modded server (published Modrinth pack)
|
||||
- Modded server (uploaded custom pack)
|
||||
|
||||
Each type defines different requirements to join. Vanilla servers specify supported and recommended Minecraft versions. Modded servers either link a Modrinth pack or upload a custom pack, which enforces the required version and mod loader.
|
||||
|
||||
<div class="video-wrapper mb-8">
|
||||
<video autoplay loop muted playsinline>
|
||||
<source src="./compatability-type-config.mp4" type="video/mp4" />
|
||||
</video>
|
||||
</div>
|
||||
|
||||
Server Projects also introduce some new fields used for discovery and project pages:
|
||||
|
||||
- Banner
|
||||
- Country (where it is hosted)
|
||||
- Language
|
||||
- Java address
|
||||
- Bedrock address (not used yet)
|
||||
- Server compatibility
|
||||
|
||||
Additionally, Server Projects are the only project type **not eligible for payouts**. They do not earn revenue from views or downloads on the project itself. Any required content they point to, such as a modpack, receives the download and associated revenue.
|
||||
|
||||
## Project Discovery
|
||||
|
||||
Server Projects use two new discovery metrics instead of downloads to help surface new servers over time. These are:
|
||||
|
||||
- **Players online:** The live player count reported by the server.
|
||||
- **Verified plays:** Joins from the Modrinth App in the last two weeks.
|
||||
|
||||

|
||||
|
||||
Server Projects also have their own set of filters to make finding the right server easier:
|
||||
|
||||
- Type (vanilla vs modded)
|
||||
- Features
|
||||
- Gameplay
|
||||
- Meta
|
||||
- Community
|
||||
- Game version
|
||||
- Country
|
||||
- Language
|
||||
|
||||
Additionally, Server Projects are different from other project types because they’re live experiences. If they aren’t joinable, they don’t provide value. To keep discovery healthy, servers that aren’t pingable for a sustained period are removed from discovery.
|
||||
|
||||
## Joining a Server
|
||||
|
||||
Joining a server is where this all comes together. While in the app, clicking play on a server triggers different flows depending on the server type:
|
||||
|
||||
1. Vanilla servers immediately create a Fabric instance in the app using the recommended version set by the author.
|
||||
2. Modded servers show a modal which displays the required content. Clicking install creates an instance with that content.
|
||||
|
||||

|
||||
|
||||
Once installation finishes, you’ll see a completion toast that, when clicked, skips the multiplayer screen and loads you directly into the server. After the initial setup, clicking play always boots straight into the server.
|
||||
|
||||
Additionally, if you click play from the website, we’ll deep link into the app if it’s installed.
|
||||
|
||||
### Linked Server Instances
|
||||
|
||||
As mentioned earlier, joining a server creates an instance. These are called linked server instances and are similar to linked modpack instances.
|
||||
|
||||
Key differences:
|
||||
|
||||
- It’s linked to a server project, not a modpack project.
|
||||
- You can only add client-side mods. You can unlink it in settings to convert it into a linked modpack instance, but it will stop receiving server updates.
|
||||
- It always enforces the required version. If an update is available, you must accept it before launching again.
|
||||
|
||||
### Server Project Updates
|
||||
|
||||
When a server updates its compatibility, such as publishing a new modpack version, that update is distributed to all linked instances. The next time a player launches, they’re prompted to accept the changes before joining. This keeps the server and associated instances in sync.
|
||||
|
||||

|
||||
|
||||
—
|
||||
|
||||
That’s all from us! Thank you so much for your continued support and happy playing!
|
||||
@@ -11,6 +11,7 @@ import { article as creator_withdrawals_overhaul } from "./creator_withdrawals_o
|
||||
import { article as design_refresh } from "./design_refresh";
|
||||
import { article as download_adjustment } from "./download_adjustment";
|
||||
import { article as free_server_medal } from "./free_server_medal";
|
||||
import { article as introducing_server_projects } from "./introducing_server_projects";
|
||||
import { article as knossos_v2_1_0 } from "./knossos_v2_1_0";
|
||||
import { article as licensing_guide } from "./licensing_guide";
|
||||
import { article as modpack_changes } from "./modpack_changes";
|
||||
@@ -47,6 +48,7 @@ export const articles = [
|
||||
design_refresh,
|
||||
download_adjustment,
|
||||
free_server_medal,
|
||||
introducing_server_projects,
|
||||
knossos_v2_1_0,
|
||||
licensing_guide,
|
||||
modpack_changes,
|
||||
|
||||
12
packages/blog/compiled/introducing_server_projects.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// AUTO-GENERATED FILE - DO NOT EDIT
|
||||
export const article = {
|
||||
html: () => import(`./introducing_server_projects.content`).then(m => m.html),
|
||||
title: "Introducing Server Projects",
|
||||
summary: "A new project type made for seamless modded multiplayer on Modrinth.",
|
||||
date: "2026-03-02T15:00:00.000Z",
|
||||
slug: "introducing-server-projects",
|
||||
authors: ["AJfd8YH6","6EjnV9Uf","xSQqYYIN"],
|
||||
unlisted: false,
|
||||
thumbnail: true,
|
||||
|
||||
};
|
||||
|
After Width: | Height: | Size: 113 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 102 KiB |
BIN
packages/blog/public/introducing-server-projects/thumbnail.webp
Normal file
|
After Width: | Height: | Size: 115 KiB |
@@ -1,5 +1,8 @@
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import { escapeAttrValue, FilterXSS, safeAttrValue, whiteList } from 'xss'
|
||||
import xss from 'xss'
|
||||
|
||||
// @ts-expect-error xss types don't reflect CJS default export shape
|
||||
const { escapeAttrValue, FilterXSS, safeAttrValue, whiteList } = xss
|
||||
|
||||
export const configuredXss = new FilterXSS({
|
||||
whiteList: {
|
||||
|
||||