diff --git a/packages/vue/src/composables/useRenderInfo/index.test.ts b/packages/vue/src/composables/useRenderInfo/index.test.ts
new file mode 100644
index 0000000..1a13191
--- /dev/null
+++ b/packages/vue/src/composables/useRenderInfo/index.test.ts
@@ -0,0 +1,100 @@
+import { describe, it, expect } from 'vitest';
+import { useRenderInfo } from '.';
+import { defineComponent, nextTick, ref } from 'vue';
+import { mount } from '@vue/test-utils';
+
+const NamedComponentStub = defineComponent({
+ name: 'ComponentStub',
+ setup() {
+ const info = useRenderInfo();
+ const visibleCount = ref(0);
+ const hiddenCount = ref(0);
+
+ return { info, visibleCount, hiddenCount };
+ },
+ template: `
{{ visibleCount }}
`,
+});
+
+const UnnamedComponentStub = defineComponent({
+ setup() {
+ const info = useRenderInfo();
+ const visibleCount = ref(0);
+ const hiddenCount = ref(0);
+
+ return { info, visibleCount, hiddenCount };
+ },
+ template: `{{ visibleCount }}
`,
+});
+
+describe('useRenderInfo', () => {
+ it('return uid if component name is not available', async () => {
+ const wrapper = mount(UnnamedComponentStub);
+
+ expect(wrapper.vm.info.component).toBe(wrapper.vm.$.uid);
+ });
+
+ it('return render info for the given instance', async () => {
+ const wrapper = mount(NamedComponentStub);
+
+ // Initial render
+ expect(wrapper.vm.info.component).toBe('ComponentStub');
+ expect(wrapper.vm.info.count.value).toBe(1);
+ expect(wrapper.vm.info.duration.value).toBeGreaterThan(0);
+ expect(wrapper.vm.info.lastRendered).toBeGreaterThan(0);
+
+ let lastRendered = wrapper.vm.info.lastRendered;
+ let duration = wrapper.vm.info.duration.value;
+
+ // Will not trigger a render
+ wrapper.vm.hiddenCount++;
+ await nextTick();
+
+ expect(wrapper.vm.info.component).toBe('ComponentStub');
+ expect(wrapper.vm.info.count.value).toBe(1);
+ expect(wrapper.vm.info.duration.value).toBe(duration);
+ expect(wrapper.vm.info.lastRendered).toBe(lastRendered);
+
+ // Will trigger a render
+ wrapper.vm.visibleCount++;
+ await nextTick();
+
+ expect(wrapper.vm.info.component).toBe('ComponentStub');
+ expect(wrapper.vm.info.count.value).toBe(2);
+ expect(wrapper.vm.info.duration.value).not.toBe(duration);
+ expect(wrapper.vm.info.lastRendered).toBeGreaterThan(0);
+ });
+
+ it('can be used with a specific component instance', async () => {
+ const wrapper = mount(NamedComponentStub);
+ const instance = wrapper.vm.$;
+
+ const info = useRenderInfo(instance);
+
+ // Initial render (should be zero because the component has already rendered on mount)
+ expect(info.component).toBe('ComponentStub');
+ expect(info.count.value).toBe(0);
+ expect(info.duration.value).toBe(0);
+ expect(info.lastRendered).toBeGreaterThan(0);
+
+ let lastRendered = info.lastRendered;
+ let duration = info.duration.value;
+
+ // Will not trigger a render
+ wrapper.vm.hiddenCount++;
+ await nextTick();
+
+ expect(info.component).toBe('ComponentStub');
+ expect(info.count.value).toBe(0);
+ expect(info.duration.value).toBe(duration);
+ expect(info.lastRendered).toBe(lastRendered);
+
+ // Will trigger a render
+ wrapper.vm.visibleCount++;
+ await nextTick();
+
+ expect(info.component).toBe('ComponentStub');
+ expect(info.count.value).toBe(1);
+ expect(info.duration.value).not.toBe(duration);
+ expect(info.lastRendered).toBeGreaterThan(0);
+ });
+});
\ No newline at end of file
diff --git a/packages/vue/src/composables/useRenderInfo/index.ts b/packages/vue/src/composables/useRenderInfo/index.ts
new file mode 100644
index 0000000..7d91b55
--- /dev/null
+++ b/packages/vue/src/composables/useRenderInfo/index.ts
@@ -0,0 +1,38 @@
+import { onBeforeMount, onBeforeUpdate, onMounted, onUpdated, readonly, ref, type ComponentInternalInstance } from 'vue';
+import { useRenderCount } from '../useRenderCount';
+import { getLifeCycleTarger } from '../..';
+
+/**
+ * @name useRenderInfo
+ * @category Components
+ * @description Returns information about the component's render count and the last time it was rendered
+ *
+ * @param {ComponentInternalInstance} [instance] The component instance to track the render count for
+ *
+ *
+ * @example
+ * const { component, count, duration, lastRendered } = useRenderInfo();
+ *
+ * @example
+ * const { component, count, duration, lastRendered } = useRenderInfo(getCurrentInstance());
+ */
+export function useRenderInfo(instance?: ComponentInternalInstance) {
+ const target = getLifeCycleTarger(instance);
+ const duration = ref(0);
+
+ const startMark = () => duration.value = performance.now();
+ const endMark = () => duration.value = Math.max(performance.now() - duration.value, 0);
+
+ onBeforeMount(startMark, target);
+ onMounted(endMark, target);
+
+ onBeforeUpdate(startMark, target);
+ onUpdated(endMark, target);
+
+ return {
+ component: target?.type.name ?? target?.uid,
+ count: useRenderCount(instance),
+ duration: readonly(duration),
+ lastRendered: Date.now(),
+ };
+}
\ No newline at end of file