diff --git a/e2e/docs/components/route-link.md b/e2e/docs/components/route-link.md
new file mode 100644
index 0000000000..8083152ee4
--- /dev/null
+++ b/e2e/docs/components/route-link.md
@@ -0,0 +1,49 @@
+## RouteLink
+
+### Home Page
+
+- text
+- text
+- text
+
+### Non-Existent
+
+- text
+- text
+- text
+
+### Non-ASCII
+
+- text
+- text
+- text
+
+### Non-ASCII Encoded
+
+- text
+- text
+- text
+
+### Active
+
+- text
+- text
+- text
+- text
+
+### Class
+
+- text
+- text
+
+### Attrs
+
+- text
+- text
+- text
+- text
+
+### Slots
+
+- text
+- texttext
diff --git a/e2e/tests/components/route-link.cy.ts b/e2e/tests/components/route-link.cy.ts
new file mode 100644
index 0000000000..86b937536d
--- /dev/null
+++ b/e2e/tests/components/route-link.cy.ts
@@ -0,0 +1,87 @@
+it('RouteLink', () => {
+ const E2E_BASE = Cypress.env('E2E_BASE')
+
+ cy.visit('/components/route-link.html')
+
+ cy.get(`.e2e-theme-content #home-page + ul > li`).each((el) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should('have.attr', 'href', E2E_BASE)
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ cy.get(`.e2e-theme-content #non-existent + ul > li`).each((el) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should('have.attr', 'href', `${E2E_BASE}non-existent.html`)
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ cy.get(`.e2e-theme-content #non-ascii + ul > li`).each((el) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should(
+ 'have.attr',
+ 'href',
+ encodeURI(
+ `${E2E_BASE}routes/non-ascii-paths/中文目录名/中文文件名.html`,
+ ),
+ )
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ cy.get(`.e2e-theme-content #non-ascii-encoded + ul > li`).each((el) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should(
+ 'have.attr',
+ 'href',
+ encodeURI(
+ `${E2E_BASE}routes/non-ascii-paths/中文目录名/中文文件名.html`,
+ ),
+ )
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ cy.get(`.e2e-theme-content #active + ul > li`).each((el, index) => {
+ cy.wrap(el).within(() => {
+ if (index < 2) {
+ cy.get('a').should('have.attr', 'class', 'route-link route-link-active')
+ } else {
+ cy.get('a').should('have.attr', 'class', 'route-link')
+ }
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ const classResults = [
+ 'route-link custom-class',
+ 'route-link route-link-active custom-class',
+ ]
+ cy.get(`.e2e-theme-content #class + ul > li`).each((el, index) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should('have.attr', 'class', classResults[index])
+ cy.get('a').should('have.text', 'text')
+ })
+ })
+
+ const attrName = ['title', 'target', 'rel', 'aria-label']
+ const attrValue = ['Title', '_blank', 'noopener', 'test']
+
+ cy.get(`.e2e-theme-content #attrs + ul > li`).each((el, index) => {
+ cy.wrap(el).within(() => {
+ cy.get('a').should('have.attr', attrName[index], attrValue[index])
+ })
+ })
+
+ cy.get(`.e2e-theme-content #slots + ul > li`).each((el, index) => {
+ cy.wrap(el).within(() => {
+ cy.get('a')
+ .children()
+ .should('have.lengthOf', index + 1)
+ .each((el) => {
+ cy.wrap(el).contains('span', 'text')
+ })
+ })
+ })
+})
diff --git a/packages/client/src/components/RouteLink.ts b/packages/client/src/components/RouteLink.ts
index 0acef8f188..758f0aa5d3 100644
--- a/packages/client/src/components/RouteLink.ts
+++ b/packages/client/src/components/RouteLink.ts
@@ -81,3 +81,8 @@ export const RouteLink: FunctionalComponent<
}
RouteLink.displayName = 'RouteLink'
+RouteLink.props = {
+ active: Boolean,
+ activeClass: String,
+ to: String,
+}