diff --git a/transformations/__tests__/global-filter.spec.ts b/transformations/__tests__/global-filter.spec.ts
new file mode 100644
index 0000000..3653e4f
--- /dev/null
+++ b/transformations/__tests__/global-filter.spec.ts
@@ -0,0 +1,37 @@
+import { defineInlineTest } from 'jscodeshift/src/testUtils'
+const transform = require('../global-filter')
+
+defineInlineTest(
+  transform,
+  {},
+  `const app = Vue.createApp(App)
+Vue.filter('capitalize', function(value) {
+  return value
+})`,
+  `const app = Vue.createApp(App)
+app.config.globalProperties.$filters = {
+  capitalize(value) {
+    return value
+  }
+};`,
+  'transform global filter'
+)
+
+defineInlineTest(
+  transform,
+  {},
+  `const app = new Vue(App)
+Vue.filter('capitalize', function(value) {
+  return value
+})`,
+  `const app = new Vue(App)
+Vue.filter('capitalize', function(value) {
+  return value
+})
+`,
+  'transform global filter(no effect and will warn)'
+)
+
+
+
+
diff --git a/transformations/global-filter.ts b/transformations/global-filter.ts
new file mode 100644
index 0000000..183a161
--- /dev/null
+++ b/transformations/global-filter.ts
@@ -0,0 +1,91 @@
+import wrap from '../src/wrapAstTransformation'
+import type { ASTTransformation } from '../src/wrapAstTransformation'
+
+export const transformAST: ASTTransformation = ({ root, j }) => {
+//  find the createApp()
+  const appDeclare = root.find(j.VariableDeclarator, {
+    id: { type: 'Identifier' },
+    init: {
+      type: 'CallExpression',
+      callee: {
+        object: {
+          name: 'Vue'
+        },
+        property: {
+          name: 'createApp'
+        }
+      }
+    }
+  })
+
+  if (!appDeclare.length) {
+    //dont transform new Vue(...) => Vue.createApp(...)?
+    const newVue = root.find(j.NewExpression, {
+      callee: {
+        type: 'Identifier',
+        name: 'Vue'
+      }
+    })
+
+    // need to transform global-filter first
+    if (newVue.length) {
+      console.warn('please transform new-global-api before transform global-filter!')
+    }
+    return
+  }
+  const appName = appDeclare.at(0).get().node.id.name
+
+  // Vue.filter('filterName', function(value) {}) =>
+  // app.config.globalProperties.$filters = { filterName(value) {} }
+  const filters = root.find(j.ExpressionStatement, {
+    expression: {
+      type: 'CallExpression',
+      callee: {
+        type: 'MemberExpression',
+        object: { type: 'Identifier', name: 'Vue' },
+        property: { type: 'Identifier', name: 'filter' }
+      }
+    }
+  })
+  if (!filters.length) {
+    return
+  }
+
+  const methods = []
+  for (let i = 0; i < filters.length; i++) {
+    const filter = filters.at(i)
+    const args = filter.get().node.expression.arguments
+
+    methods.push(
+      j.objectMethod(
+        'method',
+        j.identifier(args[0].value),
+        args[1].params,
+        args[1].body
+      )
+    )
+  }
+
+  filters
+    .at(0)
+    .insertBefore(
+      j.expressionStatement(
+        j.assignmentExpression(
+          '=',
+          j.memberExpression(
+            j.identifier(appName),
+            j.identifier('config.globalProperties.$filters'),
+            false
+          ),
+          j.objectExpression(methods)
+        )
+      )
+    )
+
+  for (let i = 0; i < filters.length; i++) {
+    filters.at(i).remove()
+  }
+}
+
+export default wrap(transformAST)
+export const parser = 'babylon'
diff --git a/transformations/index.ts b/transformations/index.ts
index 8ec4e18..e2066bb 100644
--- a/transformations/index.ts
+++ b/transformations/index.ts
@@ -17,6 +17,7 @@ const transformationMap: {
   'scoped-slots-to-slots': require('./scoped-slots-to-slots'),
   'new-directive-api': require('./new-directive-api'),
   'remove-vue-set-and-delete': require('./remove-vue-set-and-delete'),
+  'global-filter': require('./global-filter'),
 
   // atomic ones
   'remove-contextual-h-from-render': require('./remove-contextual-h-from-render'),