diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9a472acb..08fc76db 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -76,6 +76,7 @@
"nprogress",
"pnpm",
"portfinder",
+ "qrcode",
"shiki",
"shikiji",
"shikijs",
diff --git a/docs/.vuepress/collections/en/theme-guide.ts b/docs/.vuepress/collections/en/theme-guide.ts
index 26e945ac..8f85637d 100644
--- a/docs/.vuepress/collections/en/theme-guide.ts
+++ b/docs/.vuepress/collections/en/theme-guide.ts
@@ -58,6 +58,7 @@ export const themeGuide: ThemeCollectionItem = defineCollection({
'code-tree',
'field',
'tabs',
+ 'qrcode',
'timeline',
'demo-wrapper',
'flex',
diff --git a/docs/.vuepress/collections/zh/theme-guide.ts b/docs/.vuepress/collections/zh/theme-guide.ts
index bcfadf05..d87c7c98 100644
--- a/docs/.vuepress/collections/zh/theme-guide.ts
+++ b/docs/.vuepress/collections/zh/theme-guide.ts
@@ -58,6 +58,7 @@ export const themeGuide: ThemeCollectionItem = defineCollection({
'code-tree',
'field',
'tabs',
+ 'qrcode',
'timeline',
'demo-wrapper',
'flex',
diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts
index bab440aa..d026ad8f 100644
--- a/docs/.vuepress/theme.ts
+++ b/docs/.vuepress/theme.ts
@@ -36,6 +36,7 @@ export const theme: Theme = plumeTheme({
imageSize: 'all',
mark: 'lazy',
pdf: true,
+ qrcode: true,
caniuse: true,
acfun: true,
bilibili: true,
diff --git a/docs/en/guide/markdown/qrcode.md b/docs/en/guide/markdown/qrcode.md
new file mode 100644
index 00000000..43673379
--- /dev/null
+++ b/docs/en/guide/markdown/qrcode.md
@@ -0,0 +1,238 @@
+---
+title: qrcode
+icon: uiw:qrcode
+createTime: 2025/12/05 16:16:06
+permalink: /en/guide/markdown/qrcode/
+badge: 新
+---
+
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+
+## Overview
+
+In Markdown, you can embed QR code images generated from text using simple syntax, allowing them to be scanned when needed.
+
+The text can be:
+
+- A remotely accessible link address
+- A path to a `.md` file within the VuePress site _(both absolute and relative paths are supported)_
+- Any plain text _(avoid overly long text)_
+
+## Configuration
+
+This feature is disabled by default. You need to enable it in the `theme` configuration.
+
+```ts title=".vuepress/config.ts"
+export default defineUserConfig({
+ theme: plumeTheme({
+ markdown: {
+ qrcode: true, // [!code ++]
+ }
+ })
+})
+```
+
+## Syntax
+
+### Inline Syntax
+
+Inline syntax is suitable for shorter `text`, such as links.
+
+```md
+
+@[qrcode](text)
+
+@[qrcode card svg title="xxx" align="center"](text)
+```
+
+### Container Syntax
+
+Container syntax is suitable for longer `text`, such as paragraphs or multi-line text.
+
+```md
+::: qrcode card svg title="xxx" align="center"
+text
+:::
+```
+
+::: warning QR codes generated from overly long text may be truncated and potentially unscannable.
+:::
+
+## Attributes
+
+:::: field-group
+::: field name="card" type="boolean" optional default="false"
+Whether to enable the card style.
+:::
+::: field name="svg" type="boolean" optional default="false"
+Whether to render the QR code in SVG format. The default format is PNG.
+:::
+::: field name="title" type="string" optional
+The title of the QR code.
+:::
+::: field name="align" type="'left' | 'center' | 'right'" optional default="left"
+The alignment of the QR code.
+:::
+::: field name="width" type="number" optional default="300"
+The width of the QR code.
+:::
+::::
+
+The following attribute configurations directly affect the final rendering of the QR code.
+Usually, the default values are sufficient and do not require configuration.
+
+:::: field-group
+::: field name="light" type="string" optional default="#ffffffff"
+The color for the light parts of the QR code, i.e., the background color.
+:::
+::: field name="dark" type="string" optional default="#000000ff"
+The color for the dark parts of the QR code, i.e., the QR code color.
+:::
+::: field name="margin" type="number" optional default="2"
+The margin of the QR code.
+:::
+::: field name="level" type="'L' | 'M' | 'Q' | 'H'" optional default="M"
+**Error Correction Level**
+
+Error correction allows the QR code to be successfully scanned even if it is dirty or damaged.
+Four levels are available depending on the operating environment.
+
+Higher levels provide better error resistance but reduce the data capacity of the symbol.
+
+If the QR code symbol is unlikely to be damaged, lower error correction levels like Low or Medium can be safely used.
+:::
+::: field name="version" type="number" optional
+**QR Code Version**
+
+If not specified, a more suitable value will be automatically calculated. Valid range: `1-40`.
+:::
+::: field name="scale" type="number" optional default="4"
+Scaling factor. A value of 1 means 1 pixel per module (black dot).
+:::
+::: field name="mask" type="1 | 2 | 3 | 4 | 5 | 6 | 7" optional
+The mask pattern used to mask the symbol.
+
+If not specified, a more suitable value will be automatically calculated.
+::::
+
+## Examples
+
+### Accessible Remote Link
+
+**Input:**
+
+```md
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+```
+
+**Output:**
+
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+
+### Internal Page Path
+
+**Input:**
+
+```md
+@[qrcode](.)
+@[qrcode](./steps.md)
+@[qrcode](/guide/markdown/qrcode.md)
+```
+
+**Output:**
+
+::: flex
+@[qrcode](.)
+@[qrcode](./steps.md)
+@[qrcode](/guide/markdown/qrcode.md)
+:::
+
+### Arbitrary Text
+
+**Input:**
+
+```md
+@[qrcode](vuepress-theme-plume is an open-source VuePress theme)
+```
+
+**Output:**
+
+@[qrcode](vuepress-theme-plume is an open-source VuePress theme)
+
+**Input:**
+
+```md
+::: qrcode title="The Road Not Taken · by Robert Frost"
+Two roads diverged in a yellow wood,
+And sorry I could not travel both
+And be one traveler, long I stood
+And looked down one as far as I could
+To where it bent in the undergrowth;
+
+Then took the other, as just as fair,
+And having perhaps the better claim,
+Because it was grassy and wanted wear;
+Though as for that the passing there
+Had worn them really about the same,
+
+And both that morning equally lay
+In leaves no step had trodden black.
+Oh, I kept the first for another day!
+Yet knowing how way leads on to way,
+I doubted if I should ever come back.
+
+I shall be telling this with a sigh
+Somewhere ages and ages hence:
+Two roads diverged in a wood, and I—
+I took the one less traveled by,
+And that has made all the difference.
+:::
+```
+
+**Output:**
+
+::: qrcode title="The Road Not Taken · by Robert Frost"
+Two roads diverged in a yellow wood,
+And sorry I could not travel both
+And be one traveler, long I stood
+And looked down one as far as I could
+To where it bent in the undergrowth;
+
+Then took the other, as just as fair,
+And having perhaps the better claim,
+Because it was grassy and wanted wear;
+Though as for that the passing there
+Had worn them really about the same,
+
+And both that morning equally lay
+In leaves no step had trodden black.
+Oh, I kept the first for another day!
+Yet knowing how way leads on to way,
+I doubted if I should ever come back.
+
+I shall be telling this with a sigh
+Somewhere ages and ages hence:
+Two roads diverged in a wood, and I—
+I took the one less traveled by,
+And that has made all the difference.
+:::
+
+### QR Code Card with Information
+
+**Input:**
+
+```md
+@[qrcode card title="vuepress-theme-plume"](https://github.com/pengzhanbo/vuepress-theme-plume)
+```
+
+Equivalent to:
+
+```md
+::: qrcode card title="vuepress-theme-plume"
+https://github.com/pengzhanbo/vuepress-theme-plume
+:::
+```
+
+**Output:**
+
+@[qrcode card title="vuepress-theme-plume"](https://github.com/pengzhanbo/vuepress-theme-plume)
diff --git a/docs/guide/markdown/qrcode.md b/docs/guide/markdown/qrcode.md
new file mode 100644
index 00000000..bfdcf3e2
--- /dev/null
+++ b/docs/guide/markdown/qrcode.md
@@ -0,0 +1,204 @@
+---
+title: 二维码
+icon: uiw:qrcode
+createTime: 2025/12/05 10:37:43
+permalink: /guide/markdown/qrcode/
+badge: 新
+---
+
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+
+## 概述
+
+在 Markdown 中,通过简单的语法,可以在文档中插入由文本转换成的二维码图片,以便在需要时进行扫描。
+
+文本可以是:
+
+- 远程可访问的链接地址
+- vuepress 站点内的 `.md` 文件路径 _(绝对路径 或相对路径 均支持)_
+- 任意普通文本 (避免过长的文本)
+
+## 配置
+
+该功能默认不启用,你需要在 `theme` 配置中启用。
+
+```ts title=".vuepress/config.ts"
+export default defineUserConfig({
+ theme: plumeTheme({
+ markdown: {
+ qrcode: true, // [!code ++]
+ }
+ })
+})
+```
+
+## 语法
+
+### 单行语法
+
+单行语法适用于 `text` 文本较短时,比如 链接 等。
+
+```md
+
+@[qrcode](text)
+
+@[qrcode card svg title="xxx" align="center"](text)
+```
+
+### 容器语法
+
+容器语法适用于 `text` 文本较长时,比如 段落,多行文本 等。
+
+```md
+::: qrcode card svg title="xxx" align="center"
+text
+:::
+```
+
+::: warning 过长的文本生成的二维码可能会被截断,且可能无法扫描
+:::
+
+## 属性
+
+:::: field-group
+::: field name="card" type="boolean" optional default="false"
+是否启用卡片样式。
+:::
+::: field name="svg" type="boolean" optional default="false"
+是否将二维码渲染为 SVG 格式。默认渲染为 PNG 格式。
+:::
+::: field name="title" type="string" optional
+二维码标题。
+:::
+::: field name="align" type="'left' | 'center' | 'right'" optional default="left"
+二维码对齐方式。
+:::
+::: field name="width" type="number" optional default="300"
+二维码宽度。
+:::
+::::
+
+以下属性配置将直接影响二维码的最终渲染效果,通常使用默认值即可,无需配置。
+
+:::: field-group
+::: field name="light" type="string" optional default="#ffffffff"
+二维码亮色部分颜色,即背景色
+:::
+::: field name="dark" type="string" optional default="#000000ff"
+二维码暗色部分颜色,即二维码颜色
+:::
+::: field name="margin" type="number" optional default="2"
+二维码边距
+:::
+::: field name="level" type="'L' | 'M' | 'Q' | 'H'" optional default="M"
+**纠错等级**
+
+纠错能力使得即使二维码符号被污染或损坏,也能成功扫描。根据操作环境,有四个级别可供选择。
+
+更高级别提供更好的抗错能力,但会降低符号的容量。
+
+如果二维码符号可能被损坏的几率较低,则可以安全使用低纠错级别,如低或中。
+:::
+::: field name="version" type="number" optional
+**二维码版本**
+
+若未指定,将自动计算更合适的值。取值范围 `1-40`。
+:::
+::: field name="scale" type="number" optional default="4"
+缩放因子。值为 1 表示每个模块(黑点)对应 1 像素。
+:::
+::: field name="mask" type="1 | 2 | 3 | 4 | 5 | 6 | 7" optional
+用于遮蔽符号的掩码模式。
+
+若未指定,将自动计算更合适的值。
+::::
+
+## 示例
+
+### 可访问的远程链接
+
+**输入:**
+
+```md
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+```
+
+**输出:**
+
+@[qrcode](https://github.com/pengzhanbo/vuepress-theme-plume)
+
+### 站内的页面路径
+
+**输入:**
+
+```md
+@[qrcode](.)
+@[qrcode](./steps.md)
+@[qrcode](/guide/markdown/qrcode.md)
+```
+
+**输出:**
+
+::: flex
+@[qrcode](.)
+@[qrcode](./steps.md)
+@[qrcode](/guide/markdown/qrcode.md)
+:::
+
+### 任意文本
+
+**输入:**
+
+```md
+@[qrcode](vuepress-theme-plume 是一款开源的 VuePress 主题)
+```
+
+**输出:**
+
+@[qrcode](vuepress-theme-plume 是一款开源的 VuePress 主题)
+
+**输入:**
+
+```md
+::: qrcode title="宣州谢朓楼饯别校书叔云 <唐·李白>"
+弃我去者,昨日之日不可留。
+乱我心者,今日之日多烦忧。
+长风万里送秋雁,对此可以酣高楼。
+蓬莱文章建安骨,中间小谢又清发。
+俱怀逸兴壮思飞,欲上青天览明月。
+抽刀断水水更流,举杯消愁愁更愁。
+人生在世不称意,明朝散发弄扁舟。
+:::
+```
+
+**输出:**
+
+::: qrcode title="宣州谢朓楼饯别校书叔云 <唐·李白>"
+弃我去者,昨日之日不可留。
+乱我心者,今日之日多烦忧。
+长风万里送秋雁,对此可以酣高楼。
+蓬莱文章建安骨,中间小谢又清发。
+俱怀逸兴壮思飞,欲上青天览明月。
+抽刀断水水更流,举杯消愁愁更愁。
+人生在世不称意,明朝散发弄扁舟。
+:::
+
+### 带信息的二维码卡片
+
+**输入:**
+
+```md
+@[qrcode card title="vuepress-theme-plume"](https://github.com/pengzhanbo/vuepress-theme-plume)
+```
+
+等同于:
+
+```md
+::: qrcode card title="vuepress-theme-plume"
+https://github.com/pengzhanbo/vuepress-theme-plume
+:::
+```
+
+**输出:**
+
+@[qrcode card title="vuepress-theme-plume"](https://github.com/pengzhanbo/vuepress-theme-plume)
diff --git a/package.json b/package.json
index 9e86ce69..bb8442e5 100644
--- a/package.json
+++ b/package.json
@@ -50,6 +50,7 @@
"@types/minimist": "catalog:dev",
"@types/node": "catalog:dev",
"@types/picomatch": "catalog:dev",
+ "@types/qrcode": "catalog:dev",
"@types/stylus": "catalog:dev",
"@types/three": "catalog:dev",
"@types/webpack-env": "catalog:dev",
diff --git a/plugins/plugin-md-power/__test__/__snapshots__/qrcodePlugin.spec.ts.snap b/plugins/plugin-md-power/__test__/__snapshots__/qrcodePlugin.spec.ts.snap
new file mode 100644
index 00000000..98835b38
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/__snapshots__/qrcodePlugin.spec.ts.snap
@@ -0,0 +1,13 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`qrcodePlugin > should not work with container syntax 1`] = `""`;
+
+exports[`qrcodePlugin > should not work with container syntax 2`] = `""`;
+
+exports[`qrcodePlugin > should not work with container syntax 3`] = `""`;
+
+exports[`qrcodePlugin > should work with embed syntax 1`] = `""`;
+
+exports[`qrcodePlugin > should work with embed syntax 2`] = `""`;
+
+exports[`qrcodePlugin > should work with embed syntax 3`] = `""`;
diff --git a/plugins/plugin-md-power/__test__/qrcodePlugin.spec.ts b/plugins/plugin-md-power/__test__/qrcodePlugin.spec.ts
new file mode 100644
index 00000000..dd57781c
--- /dev/null
+++ b/plugins/plugin-md-power/__test__/qrcodePlugin.spec.ts
@@ -0,0 +1,27 @@
+import MarkdownIt from 'markdown-it'
+import { describe, expect, it } from 'vitest'
+import { qrcodePlugin } from '../src/node/embed/qrcode.js'
+
+describe('qrcodePlugin', () => {
+ it('should work with embed syntax', () => {
+ const md = MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(qrcodePlugin)
+
+ expect(md.render('@[qrcode](text)')).toMatchSnapshot()
+ expect(md.render('@[qrcode svg card](text)')).toMatchSnapshot()
+ expect(md.render('@[qrcode title="title"](text)')).toMatchSnapshot()
+ })
+
+ it('should not work with container syntax', () => {
+ const md = MarkdownIt().use((md) => {
+ md.block.ruler.before('code', 'import_code', () => false)
+ md.renderer.rules.import_code = () => ''
+ }).use(qrcodePlugin)
+
+ expect(md.render(':::qrcode\ntext\n:::')).toMatchSnapshot()
+ expect(md.render(':::qrcode svg card\ntext\n:::')).toMatchSnapshot()
+ expect(md.render(':::qrcode title="title"\ntext\n:::')).toMatchSnapshot()
+ })
+})
diff --git a/plugins/plugin-md-power/package.json b/plugins/plugin-md-power/package.json
index db35f12d..714d52f2 100644
--- a/plugins/plugin-md-power/package.json
+++ b/plugins/plugin-md-power/package.json
@@ -103,6 +103,7 @@
"markdown-it-cjk-friendly": "catalog:prod",
"markdown-it-container": "catalog:prod",
"nanoid": "catalog:prod",
+ "qrcode": "catalog:prod",
"shiki": "catalog:prod",
"tm-grammars": "catalog:prod",
"tm-themes": "catalog:prod",
diff --git a/plugins/plugin-md-power/src/client/components/VPQRCode.vue b/plugins/plugin-md-power/src/client/components/VPQRCode.vue
new file mode 100644
index 00000000..e3ddc0b8
--- /dev/null
+++ b/plugins/plugin-md-power/src/client/components/VPQRCode.vue
@@ -0,0 +1,228 @@
+
+
+
+