diff --git a/.gitattributes b/.gitattributes
index 7415f78..e84f95f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -20,6 +20,7 @@ packages/all/src/__tests__/tsconfig.json linguist-generated
 packages/all/src/__tests__/typebox.test.ts linguist-generated
 packages/all/src/__tests__/valibot.test.ts linguist-generated
 packages/all/src/__tests__/valita.test.ts linguist-generated
+packages/all/src/__tests__/vine.test.ts linguist-generated
 packages/all/src/__tests__/yup.test.ts linguist-generated
 packages/all/src/__tests__/zod.test.ts linguist-generated
 packages/all/src/index.ts linguist-generated
@@ -79,6 +80,7 @@ packages/main/src/__tests__/tsconfig.json linguist-generated
 packages/main/src/__tests__/typebox.test.ts linguist-generated
 packages/main/src/__tests__/valibot.test.ts linguist-generated
 packages/main/src/__tests__/valita.test.ts linguist-generated
+packages/main/src/__tests__/vine.test.ts linguist-generated
 packages/main/src/__tests__/yup.test.ts linguist-generated
 packages/main/src/__tests__/zod.test.ts linguist-generated
 packages/main/src/adapters.ts linguist-generated
@@ -114,6 +116,10 @@ packages/valita/README.md linguist-generated
 packages/valita/src/__tests__/tsconfig.json linguist-generated
 packages/valita/src/index.ts linguist-generated
 packages/valita/tsconfig.json linguist-generated
+packages/vine/README.md linguist-generated
+packages/vine/src/__tests__/tsconfig.json linguist-generated
+packages/vine/src/index.ts linguist-generated
+packages/vine/tsconfig.json linguist-generated
 packages/yup/README.md linguist-generated
 packages/yup/src/__tests__/tsconfig.json linguist-generated
 packages/yup/src/index.ts linguist-generated
diff --git a/README.md b/README.md
index bcd5998..839669f 100644
--- a/README.md
+++ b/README.md
@@ -234,6 +234,16 @@ We value flexibility, which is why there are multiple ways of using TypeSchema:
     <td align="left"><nobr><code><a href="https://github.com/decs/typeschema/tree/main/packages/fastest-validator">@typeschema/fastest-validator</a></code></nobr></td>
     <td align="right"><a href="https://www.npmjs.com/package/@typeschema/fastest-validator" rel="nofollow"><img src="https://img.shields.io/npm/dw/@typeschema/fastest-validator.svg?label=" alt="npm downloads"></a></td>
   </tr>
+  <tr>
+    <td align="left"><nobr><a href="https://vinejs.dev" rel="nofollow">vine</a></nobr></td>
+    <td align="left"><a href="https://github.com/vinejs/vine" rel="nofollow"><img src="https://img.shields.io/github/stars/vinejs/vine?style=social" alt="GitHub stars"></a></td>
+    <td align="center">✅</td>
+    <td align="center">✅</td>
+    <td align="center">✅</td>
+    <td align="center">🧐</td>
+    <td align="left"><nobr><code><a href="https://github.com/decs/typeschema/tree/main/packages/vine">@typeschema/vine</a></code></nobr></td>
+    <td align="right"><a href="https://www.npmjs.com/package/@typeschema/vine" rel="nofollow"><img src="https://img.shields.io/npm/dw/@typeschema/vine.svg?label=" alt="npm downloads"></a></td>
+  </tr>
   <tr>
     <td align="left"><nobr><a href="https://github.com/grantila/suretype" rel="nofollow">suretype</a></nobr></td>
     <td align="left"><a href="https://github.com/grantila/suretype" rel="nofollow"><img src="https://img.shields.io/github/stars/grantila/suretype?style=social" alt="GitHub stars"></a></td>
diff --git a/packages/all/package.json b/packages/all/package.json
index bac758b..2f34e81 100644
--- a/packages/all/package.json
+++ b/packages/all/package.json
@@ -29,6 +29,7 @@
     "typebox",
     "valibot",
     "valita",
+    "vine",
     "yup",
     "zod"
   ],
@@ -91,6 +92,7 @@
     "@typeschema/typebox": "workspace:*",
     "@typeschema/valibot": "workspace:*",
     "@typeschema/valita": "workspace:*",
+    "@typeschema/vine": "workspace:*",
     "@typeschema/yup": "workspace:*",
     "@typeschema/zod": "workspace:*"
   },
@@ -118,6 +120,7 @@
     "@gcornut/valibot-json-schema": "^0.0.25",
     "valibot": "^0.30.0",
     "@badrap/valita": "^0.3.6",
+    "@vinejs/vine": "^2.0.0",
     "@sodaru/yup-to-json-schema": "^2.0.1",
     "yup": "^1.4.0",
     "zod": "^3.22.4",
diff --git a/packages/all/src/__tests__/vine.test.ts b/packages/all/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..0bfa1c5
--- /dev/null
+++ b/packages/all/src/__tests__/vine.test.ts
@@ -0,0 +1,82 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+  const schema = vine.object({
+    age: vine.number(),
+    createdAt: vine.date({formats: {utc: true}}),
+    email: vine.string().email(),
+    id: vine.string(),
+    name: vine.string(),
+    updatedAt: vine.date({formats: {utc: true}}),
+  });
+
+  const data = {
+    age: 123 as string | number,
+    createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+  };
+  const outputData = {
+    age: 123,
+    createdAt: new Date('2021-01-01T00:00:00.000Z'),
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+  };
+  const badData = {
+    age: '123a',
+    createdAt: '2021-01-01T00:00:00.000Z',
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z',
+  };
+
+  test('infer', () => {
+    expectTypeOf<Infer<typeof schema>>().toEqualTypeOf(outputData);
+    expectTypeOf<InferIn<typeof schema>>().toEqualTypeOf(data);
+  });
+
+  test('validate', async () => {
+    expect(await validate(schema, data)).toStrictEqual({
+      data: outputData,
+      success: true,
+    });
+    expect(await validate(schema, badData)).toStrictEqual({
+      issues: [{message: 'The age field must be a number', path: ['age']}],
+      success: false,
+    });
+  });
+
+  test('assert', async () => {
+    expect(await assert(schema, data)).toStrictEqual(outputData);
+    await expect(assert(schema, badData)).rejects.toThrow();
+  });
+
+  test('wrap', async () => {
+    const tRPC = initTRPC.create();
+    const router = tRPC.router({
+      hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+        expectTypeOf<typeof input>().toEqualTypeOf(outputData);
+        return input;
+      }),
+    });
+    const createCaller = tRPC.createCallerFactory(router);
+    const caller = createCaller({});
+    expect(await caller.hello(data)).toStrictEqual(outputData);
+  });
+});
diff --git a/packages/main/package.json b/packages/main/package.json
index a584ad1..2d473fc 100644
--- a/packages/main/package.json
+++ b/packages/main/package.json
@@ -29,6 +29,7 @@
     "typebox",
     "valibot",
     "valita",
+    "vine",
     "yup",
     "zod"
   ],
@@ -115,6 +116,8 @@
     "valibot": "^0.30.0",
     "@typeschema/valita": "workspace:*",
     "@badrap/valita": "^0.3.6",
+    "@typeschema/vine": "workspace:*",
+    "@vinejs/vine": "^2.0.0",
     "@typeschema/yup": "workspace:*",
     "@sodaru/yup-to-json-schema": "^2.0.1",
     "yup": "^1.4.0",
@@ -139,6 +142,7 @@
     "@typeschema/typebox": "workspace:*",
     "@typeschema/valibot": "workspace:*",
     "@typeschema/valita": "workspace:*",
+    "@typeschema/vine": "workspace:*",
     "@typeschema/yup": "workspace:*",
     "@typeschema/zod": "workspace:*"
   },
@@ -191,6 +195,9 @@
     "@typeschema/valita": {
       "optional": true
     },
+    "@typeschema/vine": {
+      "optional": true
+    },
     "@typeschema/yup": {
       "optional": true
     },
diff --git a/packages/main/src/__tests__/vine.test.ts b/packages/main/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..0bfa1c5
--- /dev/null
+++ b/packages/main/src/__tests__/vine.test.ts
@@ -0,0 +1,82 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+  const schema = vine.object({
+    age: vine.number(),
+    createdAt: vine.date({formats: {utc: true}}),
+    email: vine.string().email(),
+    id: vine.string(),
+    name: vine.string(),
+    updatedAt: vine.date({formats: {utc: true}}),
+  });
+
+  const data = {
+    age: 123 as string | number,
+    createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+  };
+  const outputData = {
+    age: 123,
+    createdAt: new Date('2021-01-01T00:00:00.000Z'),
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+  };
+  const badData = {
+    age: '123a',
+    createdAt: '2021-01-01T00:00:00.000Z',
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z',
+  };
+
+  test('infer', () => {
+    expectTypeOf<Infer<typeof schema>>().toEqualTypeOf(outputData);
+    expectTypeOf<InferIn<typeof schema>>().toEqualTypeOf(data);
+  });
+
+  test('validate', async () => {
+    expect(await validate(schema, data)).toStrictEqual({
+      data: outputData,
+      success: true,
+    });
+    expect(await validate(schema, badData)).toStrictEqual({
+      issues: [{message: 'The age field must be a number', path: ['age']}],
+      success: false,
+    });
+  });
+
+  test('assert', async () => {
+    expect(await assert(schema, data)).toStrictEqual(outputData);
+    await expect(assert(schema, badData)).rejects.toThrow();
+  });
+
+  test('wrap', async () => {
+    const tRPC = initTRPC.create();
+    const router = tRPC.router({
+      hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+        expectTypeOf<typeof input>().toEqualTypeOf(outputData);
+        return input;
+      }),
+    });
+    const createCaller = tRPC.createCallerFactory(router);
+    const caller = createCaller({});
+    expect(await caller.hello(data)).toStrictEqual(outputData);
+  });
+});
diff --git a/packages/main/src/adapters.ts b/packages/main/src/adapters.ts
index 972067b..33b6ba5 100644
--- a/packages/main/src/adapters.ts
+++ b/packages/main/src/adapters.ts
@@ -18,6 +18,7 @@ import type {AdapterResolver as SuretypeResolver} from '@typeschema/suretype';
 import type {AdapterResolver as TypeboxResolver} from '@typeschema/typebox';
 import type {AdapterResolver as ValibotResolver} from '@typeschema/valibot';
 import type {AdapterResolver as ValitaResolver} from '@typeschema/valita';
+import type {AdapterResolver as VineResolver} from '@typeschema/vine';
 import type {AdapterResolver as YupResolver} from '@typeschema/yup';
 import type {AdapterResolver as ZodResolver} from '@typeschema/zod';
 
@@ -38,6 +39,7 @@ export type AdapterResolvers = {
   typebox: TypeboxResolver;
   valibot: ValibotResolver;
   valita: ValitaResolver;
+  vine: VineResolver;
   yup: YupResolver;
   zod: ZodResolver;
 };
diff --git a/packages/main/src/selector.ts b/packages/main/src/selector.ts
index 8a9a856..09a1d21 100644
--- a/packages/main/src/selector.ts
+++ b/packages/main/src/selector.ts
@@ -92,6 +92,7 @@ export type Select<TSchema> =
     : TSchema extends {kind: unknown} ? 'deepkit'
     : TSchema extends {addValidator: unknown} ? 'ow'
     : TSchema extends {toTerminals: unknown} ? 'valita'
+    : TSchema extends {bail: unknown} ? 'vine'
     : IsJSONSchema<TSchema> extends true ? 'json'
     : 'fastestValidator'
   : never;
@@ -129,6 +130,7 @@ export const select: <
         if ('kind' in schema) return is.deepkit(notJSON(schema));
         if ('addValidator' in schema) return is.ow(notJSON(schema));
         if ('toTerminals' in schema) return is.valita(notJSON(schema));
+        if ('bail' in schema) return is.vine(notJSON(schema));
         if (isJSONSchema(schema)) return is.json(schema);
         return is.fastestValidator(schema);
     }
diff --git a/packages/main/src/serialization.ts b/packages/main/src/serialization.ts
index 5aec484..a232a99 100644
--- a/packages/main/src/serialization.ts
+++ b/packages/main/src/serialization.ts
@@ -69,6 +69,7 @@ export const serializationAdapter: SerializationAdapter<AdapterResolver> = selec
   typebox: async schema => (await importTypeboxSerializationAdapter())(schema),
   valibot: async schema => (await importValibotSerializationAdapter())(schema),
   valita: unsupportedAdapter<AdapterResolvers['valita']>('@typeschema/valita'),
+  vine: unsupportedAdapter<AdapterResolvers['vine']>('@typeschema/vine'),
   yup: async schema => (await importYupSerializationAdapter())(schema),
   zod: async schema => (await importZodSerializationAdapter())(schema),
 });
diff --git a/packages/main/src/validation.ts b/packages/main/src/validation.ts
index 9ce525a..8c301ac 100644
--- a/packages/main/src/validation.ts
+++ b/packages/main/src/validation.ts
@@ -92,6 +92,11 @@ const importValitaValidationAdapter = memoize(async () => {
   return validationAdapter;
 });
 
+const importVineValidationAdapter = memoize(async () => {
+  const {validationAdapter} = await import('@typeschema/vine');
+  return validationAdapter;
+});
+
 const importYupValidationAdapter = memoize(async () => {
   const {validationAdapter} = await import('@typeschema/yup');
   return validationAdapter;
@@ -119,6 +124,7 @@ export const validationAdapter: ValidationAdapter<AdapterResolver> = select({
   typebox: async schema => (await importTypeboxValidationAdapter())(schema),
   valibot: async schema => (await importValibotValidationAdapter())(schema),
   valita: async schema => (await importValitaValidationAdapter())(schema),
+  vine: async schema => (await importVineValidationAdapter())(schema),
   yup: async schema => (await importYupValidationAdapter())(schema),
   zod: async schema => (await importZodValidationAdapter())(schema),
 });
diff --git a/packages/vine/README.md b/packages/vine/README.md
new file mode 100644
index 0000000..db42a5a
--- /dev/null
+++ b/packages/vine/README.md
@@ -0,0 +1,49 @@
+<!-- This file is generated. Do not modify it manually! -->
+
+<img src="https://typeschema.com/assets/logo.png" width="64px" alt="TypeSchema" />
+<h1>@typeschema/vine</h1>
+<p>
+  <a href="https://opensource.org/licenses/MIT" rel="nofollow"><img src="https://img.shields.io/github/license/decs/typeschema" alt="License"></a>
+  <a href="https://bundlephobia.com/package/@typeschema/vine" rel="nofollow"><img src="https://img.shields.io/bundlephobia/minzip/%40typeschema%2Fvine" alt="Bundle size"></a>
+  <a href="https://www.npmjs.com/package/@typeschema/vine" rel="nofollow"><img src="https://img.shields.io/npm/dw/@typeschema/vine.svg" alt="npm downloads"></a>
+  <a href="https://github.com/decs/typeschema/stargazers" rel="nofollow"><img src="https://img.shields.io/github/stars/decs/typeschema" alt="GitHub stars"></a>
+</p>
+<p>
+  Reusable adapter for VineJS schemas
+  <br />
+  <a href="https://typeschema.com">https://typeschema.com</a> ✨
+</p>
+
+```ts
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+
+import {wrap} from '@typeschema/vine';
+
+const schema = vine.object({name: vine.string()});
+
+const t = initTRPC.create();
+const appRouter = t.router({
+  hello: t.procedure
+    .input(wrap(schema))
+    .query(({input}) => `Hello, ${input.name}!`),
+  //         ^? {name: string}
+});
+
+```
+
+Use it directly or through [`@typeschema/main`](https://github.com/decs/typeschema/tree/main/packages/main)
+
+## Dependencies
+- [`@vinejs/vine`](https://www.npmjs.com/package/@vinejs/vine): Required for inference and validation (`^2.0.0`)
+
+## API
+
+### Inference
+- `Infer<TSchema>`: Extracts the output type of a schema
+- `InferIn<TSchema>`: Extracts the input type of a schema
+
+### Validation
+- `wrap(schema)`: Returns the wrapped schema with access to its operations
+- `validate(schema, data)`: Returns the validated data or a list of validation issues
+- `assert(schema, data)`: Returns the validated data or throws an `AggregateError`
diff --git a/packages/vine/package.json b/packages/vine/package.json
new file mode 100644
index 0000000..8ff3679
--- /dev/null
+++ b/packages/vine/package.json
@@ -0,0 +1,77 @@
+{
+  "//": "This file is partially generated. Only some fields can be modified manually!",
+  "name": "@typeschema/vine",
+  "//version": "This field is manually maintained.",
+  "version": "0.0.0",
+  "//description": "This field is manually maintained.",
+  "description": "Reusable adapter for VineJS schemas",
+  "keywords": [
+    "typescript",
+    "type",
+    "schema",
+    "adapter",
+    "validation",
+    "inference",
+    "assert"
+  ],
+  "homepage": "https://typeschema.com",
+  "license": "MIT",
+  "author": {
+    "name": "André Costa",
+    "email": "andrefonsecacosta@gmail.com"
+  },
+  "publishConfig": {
+    "access": "public",
+    "registry": "https://registry.npmjs.org/"
+  },
+  "files": [
+    "/dist"
+  ],
+  "main": "dist/index.js",
+  "module": "dist/index.mjs",
+  "types": "dist/index.d.ts",
+  "exports": {
+    ".": {
+      "import": {
+        "types": "./dist/index.d.mts",
+        "default": "./dist/index.mjs"
+      },
+      "require": {
+        "types": "./dist/index.d.ts",
+        "default": "./dist/index.js"
+      }
+    }
+  },
+  "sideEffects": false,
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/decs/typeschema.git"
+  },
+  "scripts": {
+    "build": "tsup --config ../../tsup.config.ts",
+    "lint": "eslint src --fix",
+    "lint:package": "publint && attw --pack",
+    "test": "vitest --config ../../vitest.config.ts",
+    "upgrade:deps": "ncu -u --dep=dev,peer --reject ow"
+  },
+  "dependencies": {
+    "@typeschema/core": "workspace:*"
+  },
+  "//devDependencies": "This field is manually maintained.",
+  "devDependencies": {
+    "@vinejs/vine": "^2.0.0"
+  },
+  "//peerDependencies": {
+    "//": "This field is manually maintained.",
+    "@vinejs/vine": "Required for inference and validation"
+  },
+  "peerDependencies": {
+    "@vinejs/vine": "^2.0.0"
+  },
+  "//peerDependenciesMeta": "This field is manually maintained.",
+  "peerDependenciesMeta": {
+    "@vinejs/vine": {
+      "optional": true
+    }
+  }
+}
diff --git a/packages/vine/src/__tests__/example.ts b/packages/vine/src/__tests__/example.ts
new file mode 100644
index 0000000..14882d6
--- /dev/null
+++ b/packages/vine/src/__tests__/example.ts
@@ -0,0 +1,14 @@
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+
+import {wrap} from '..';
+
+const schema = vine.object({name: vine.string()});
+
+const t = initTRPC.create();
+const appRouter = t.router({
+  hello: t.procedure
+    .input(wrap(schema))
+    .query(({input}) => `Hello, ${input.name}!`),
+  //         ^? {name: string}
+});
diff --git a/packages/vine/src/__tests__/tsconfig.json b/packages/vine/src/__tests__/tsconfig.json
new file mode 100644
index 0000000..6a86fc8
--- /dev/null
+++ b/packages/vine/src/__tests__/tsconfig.json
@@ -0,0 +1,5 @@
+{
+  "//": "This file is generated. Do not modify it manually!",
+  "extends": "../../../../tsconfig.test.json",
+  "include": ["*.ts"]
+}
diff --git a/packages/vine/src/__tests__/vine.test.ts b/packages/vine/src/__tests__/vine.test.ts
new file mode 100644
index 0000000..d666c1b
--- /dev/null
+++ b/packages/vine/src/__tests__/vine.test.ts
@@ -0,0 +1,78 @@
+import type {Infer, InferIn} from '..';
+
+import {initTRPC} from '@trpc/server';
+import vine from '@vinejs/vine';
+import {expectTypeOf} from 'expect-type';
+import {describe, expect, test} from 'vitest';
+
+import {assert, validate, wrap} from '..';
+
+describe('vine', () => {
+  const schema = vine.object({
+    age: vine.number(),
+    createdAt: vine.date({formats: {utc: true}}),
+    email: vine.string().email(),
+    id: vine.string(),
+    name: vine.string(),
+    updatedAt: vine.date({formats: {utc: true}}),
+  });
+
+  const data = {
+    age: 123 as string | number,
+    createdAt: '2021-01-01T00:00:00.000Z' as string | number,
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z' as string | number,
+  };
+  const outputData = {
+    age: 123,
+    createdAt: new Date('2021-01-01T00:00:00.000Z'),
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: new Date('2021-01-01T00:00:00.000Z'),
+  };
+  const badData = {
+    age: '123a',
+    createdAt: '2021-01-01T00:00:00.000Z',
+    email: 'john.doe@test.com',
+    id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
+    name: 'John Doe',
+    updatedAt: '2021-01-01T00:00:00.000Z',
+  };
+
+  test('infer', () => {
+    expectTypeOf<Infer<typeof schema>>().toEqualTypeOf(outputData);
+    expectTypeOf<InferIn<typeof schema>>().toEqualTypeOf(data);
+  });
+
+  test('validate', async () => {
+    expect(await validate(schema, data)).toStrictEqual({
+      data: outputData,
+      success: true,
+    });
+    expect(await validate(schema, badData)).toStrictEqual({
+      issues: [{message: 'The age field must be a number', path: ['age']}],
+      success: false,
+    });
+  });
+
+  test('assert', async () => {
+    expect(await assert(schema, data)).toStrictEqual(outputData);
+    await expect(assert(schema, badData)).rejects.toThrow();
+  });
+
+  test('wrap', async () => {
+    const tRPC = initTRPC.create();
+    const router = tRPC.router({
+      hello: tRPC.procedure.input(wrap(schema)).query(({input}) => {
+        expectTypeOf<typeof input>().toEqualTypeOf(outputData);
+        return input;
+      }),
+    });
+    const createCaller = tRPC.createCallerFactory(router);
+    const caller = createCaller({});
+    expect(await caller.hello(data)).toStrictEqual(outputData);
+  });
+});
diff --git a/packages/vine/src/index.ts b/packages/vine/src/index.ts
new file mode 100644
index 0000000..c5a1586
--- /dev/null
+++ b/packages/vine/src/index.ts
@@ -0,0 +1,37 @@
+/**
+ * This file is generated. Do not modify it manually!
+ */
+
+import type {
+  InputFrom,
+  OutputFrom,
+  SchemaFrom,
+  UnknownIfNever,
+} from '@typeschema/core';
+
+import {
+  createAssert,
+  createValidate,
+  createWrap,
+} from '@typeschema/core';
+
+import {AdapterResolver} from './resolver';
+import {validationAdapter} from './validation';
+
+export type Schema = SchemaFrom<AdapterResolver>;
+export type Infer<TSchema extends Schema> = UnknownIfNever<
+  OutputFrom<AdapterResolver, TSchema>
+>;
+export type InferIn<TSchema extends Schema> = UnknownIfNever<
+  InputFrom<AdapterResolver, TSchema>
+>;
+
+export const validate = createValidate(validationAdapter);
+export const assert = createAssert(validate);
+export const wrap = createWrap(assert, validate);
+
+
+export {
+  AdapterResolver,
+  validationAdapter,
+};
diff --git a/packages/vine/src/resolver.ts b/packages/vine/src/resolver.ts
new file mode 100644
index 0000000..a8d48f8
--- /dev/null
+++ b/packages/vine/src/resolver.ts
@@ -0,0 +1,13 @@
+import type {IfDefined, Resolver} from '@typeschema/core';
+import type {BaseType, symbols} from '@vinejs/vine';
+
+export interface AdapterResolver extends Resolver {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  base: IfDefined<BaseType<any, any, any>, '@vinejs/vine'>;
+  input: this['schema'] extends this['base']
+    ? this['schema'][typeof symbols.ITYPE]
+    : never;
+  output: this['schema'] extends this['base']
+    ? this['schema'][typeof symbols.OTYPE]
+    : never;
+}
diff --git a/packages/vine/src/validation.ts b/packages/vine/src/validation.ts
new file mode 100644
index 0000000..77651ff
--- /dev/null
+++ b/packages/vine/src/validation.ts
@@ -0,0 +1,37 @@
+import type {AdapterResolver} from './resolver';
+import type {ValidationAdapter} from '@typeschema/core';
+
+import {memoize} from '@typeschema/core';
+
+const importValidationModule = memoize(async () => {
+  const {errors, Vine} = await import('@vinejs/vine');
+  return {errors, vine: new Vine()};
+});
+
+export const validationAdapter: ValidationAdapter<
+  AdapterResolver
+> = async schema => {
+  const {errors, vine} = await importValidationModule();
+  const validator = vine.compile(schema);
+  return async data => {
+    try {
+      return {
+        data: await validator.validate(data),
+        success: true,
+      };
+    } catch (error) {
+      if (error instanceof errors.E_VALIDATION_ERROR) {
+        return {
+          issues: error.messages.map(
+            ({message, field}: {message: string; field: string}) => ({
+              message,
+              path: field != null ? field.split('.') : undefined,
+            }),
+          ),
+          success: false,
+        };
+      }
+      throw error;
+    }
+  };
+};
diff --git a/packages/vine/tsconfig.json b/packages/vine/tsconfig.json
new file mode 100644
index 0000000..caf2717
--- /dev/null
+++ b/packages/vine/tsconfig.json
@@ -0,0 +1,5 @@
+{
+  "//": "This file is generated. Do not modify it manually!",
+  "extends": "../../tsconfig.json",
+  "include": ["src"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f4dfd5b..4f8ca99 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -281,6 +281,9 @@ importers:
       '@typeschema/valita':
         specifier: workspace:*
         version: link:../valita
+      '@typeschema/vine':
+        specifier: workspace:*
+        version: link:../vine
       '@typeschema/yup':
         specifier: workspace:*
         version: link:../yup
@@ -309,6 +312,9 @@ importers:
       '@sodaru/yup-to-json-schema':
         specifier: ^2.0.1
         version: 2.0.1
+      '@vinejs/vine':
+        specifier: ^2.0.0
+        version: 2.0.0
       ajv:
         specifier: ^8.12.0
         version: 8.12.0
@@ -559,12 +565,18 @@ importers:
       '@typeschema/valita':
         specifier: workspace:*
         version: link:../valita
+      '@typeschema/vine':
+        specifier: workspace:*
+        version: link:../vine
       '@typeschema/yup':
         specifier: workspace:*
         version: link:../yup
       '@typeschema/zod':
         specifier: workspace:*
         version: link:../zod
+      '@vinejs/vine':
+        specifier: ^2.0.0
+        version: 2.0.0
       ajv:
         specifier: ^8.12.0
         version: 8.12.0
@@ -699,6 +711,16 @@ importers:
         specifier: ^0.3.6
         version: 0.3.6
 
+  packages/vine:
+    dependencies:
+      '@typeschema/core':
+        specifier: workspace:*
+        version: link:../core
+    devDependencies:
+      '@vinejs/vine':
+        specifier: ^2.0.0
+        version: 2.0.0
+
   packages/yup:
     dependencies:
       '@typeschema/core':
@@ -3486,6 +3508,11 @@ packages:
       write-yaml-file: 5.0.0
     dev: true
 
+  /@poppinss/macroable@1.0.2:
+    resolution: {integrity: sha512-xhhEcEvhQC8mP5oOr5hbE4CmUgmw/IPV1jhpGg2xSkzoFrt9i8YVqBQt9744EFesi5F7pBheWozg63RUBM/5JA==}
+    engines: {node: '>=18.16.0'}
+    dev: true
+
   /@rollup/plugin-node-resolve@15.2.3(rollup@4.12.0):
     resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==}
     engines: {node: '>=14.0.0'}
@@ -4366,6 +4393,25 @@ packages:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
     dev: true
 
+  /@vinejs/compiler@2.5.0:
+    resolution: {integrity: sha512-hg4ekaB5Y2zh+IWzBiC/WCDWrIfpVnKu/ubUvelKlidc/VbulsexoFRw5kJGHZenPVI5YzNnDeTdYSALkTV7jQ==}
+    engines: {node: '>=18.0.0'}
+    dev: true
+
+  /@vinejs/vine@2.0.0:
+    resolution: {integrity: sha512-NqgT4B2uo4mMsGI8LJdpuXNnan7F3xm10+kHaXpqI0PCYpn7+Xiic6av586mmj747/qZ3iR8o4C9cL54WU1fWw==}
+    engines: {node: '>=18.16.0'}
+    dependencies:
+      '@poppinss/macroable': 1.0.2
+      '@types/validator': 13.11.9
+      '@vinejs/compiler': 2.5.0
+      camelcase: 8.0.0
+      dayjs: 1.11.10
+      dlv: 1.1.3
+      normalize-url: 8.0.1
+      validator: 13.11.0
+    dev: true
+
   /@vitest/expect@1.3.1:
     resolution: {integrity: sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==}
     dependencies:
@@ -5255,6 +5301,11 @@ packages:
     engines: {node: '>=14.16'}
     dev: true
 
+  /camelcase@8.0.0:
+    resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==}
+    engines: {node: '>=16'}
+    dev: true
+
   /can-write-to-dir@1.1.1:
     resolution: {integrity: sha512-eOgiEWqjppB+3DN/5E82EQ8dTINus8d9GXMCbEsUnp2hcUIcXmBvzWmD3tXMk3CuBK0v+ddK9qw0EAF+JVRMjQ==}
     engines: {node: '>=10.13'}
@@ -5749,6 +5800,10 @@ packages:
     engines: {node: '>= 14'}
     dev: true
 
+  /dayjs@1.11.10:
+    resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+    dev: true
+
   /debug@3.2.7:
     resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
     peerDependencies:
@@ -5908,6 +5963,10 @@ packages:
       path-type: 4.0.0
     dev: true
 
+  /dlv@1.1.3:
+    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+    dev: true
+
   /doctrine@2.1.0:
     resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
     engines: {node: '>=0.10.0'}
@@ -9047,6 +9106,11 @@ packages:
     engines: {node: '>=14.16'}
     dev: true
 
+  /normalize-url@8.0.1:
+    resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==}
+    engines: {node: '>=14.16'}
+    dev: true
+
   /npm-bundled@2.0.1:
     resolution: {integrity: sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==}
     engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
@@ -12382,6 +12446,7 @@ packages:
       '@typeschema/typebox': link:packages/typebox
       '@typeschema/valibot': link:packages/valibot
       '@typeschema/valita': link:packages/valita
+      '@typeschema/vine': link:packages/vine
       '@typeschema/yup': link:packages/yup
       '@typeschema/zod': link:packages/zod
     dev: false
@@ -12407,6 +12472,7 @@ packages:
       '@typeschema/typebox': workspace:*
       '@typeschema/valibot': workspace:*
       '@typeschema/valita': workspace:*
+      '@typeschema/vine': workspace:*
       '@typeschema/yup': workspace:*
       '@typeschema/zod': workspace:*
     peerDependenciesMeta:
@@ -12442,6 +12508,8 @@ packages:
         optional: true
       '@typeschema/valita':
         optional: true
+      '@typeschema/vine':
+        optional: true
       '@typeschema/yup':
         optional: true
       '@typeschema/zod':
diff --git a/turbo/generators/config.ts b/turbo/generators/config.ts
index abdada0..163e5fc 100644
--- a/turbo/generators/config.ts
+++ b/turbo/generators/config.ts
@@ -304,6 +304,12 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
                 name: 'fastest-validator',
                 url: 'https://github.com/icebob/fastest-validator',
               },
+              {
+                adapter: adapters.find(adapter => adapter.name === 'vine'),
+                github: 'vinejs/vine',
+                name: 'vine',
+                url: 'https://vinejs.dev',
+              },
               {
                 adapter: adapters.find(adapter => adapter.name === 'suretype'),
                 github: 'grantila/suretype',