diff --git a/ee/app/assets/javascripts/ai/tanuki_bot/components/app.vue b/ee/app/assets/javascripts/ai/tanuki_bot/components/app.vue
index 25cecfa21769006dfa1e0e48fcbf60c5c7ae801c..cf61e98d88d871df08f0dff88a2e503e3ccf61b6 100644
--- a/ee/app/assets/javascripts/ai/tanuki_bot/components/app.vue
+++ b/ee/app/assets/javascripts/ai/tanuki_bot/components/app.vue
@@ -15,7 +15,8 @@ import duoUserFeedbackMutation from 'ee/ai/graphql/duo_user_feedback.mutation.gr
 import Tracking from '~/tracking';
 import { i18n, GENIE_CHAT_RESET_MESSAGE, GENIE_CHAT_CLEAR_MESSAGE } from 'ee/ai/constants';
 import getAiSlashCommands from 'ee/ai/graphql/get_ai_slash_commands.query.graphql';
-import { TANUKI_BOT_TRACKING_EVENT_NAME, MESSAGE_TYPES } from '../constants';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { TANUKI_BOT_TRACKING_EVENT_NAME, MESSAGE_TYPES, WIDTH_OFFSET } from '../constants';
 import TanukiBotSubscriptions from './tanuki_bot_subscriptions.vue';
 
 export default {
@@ -42,7 +43,7 @@ export default {
     DuoChatCallout,
     TanukiBotSubscriptions,
   },
-  mixins: [Tracking.mixin()],
+  mixins: [Tracking.mixin(), glFeatureFlagsMixin()],
   provide() {
     return {
       renderGFM,
@@ -104,6 +105,10 @@ export default {
       cancelledRequestIds: [],
       completedRequestId: null,
       aiSlashCommands: [],
+      width: 400,
+      height: window.innerHeight,
+      minWidth: 400,
+      minHeight: 400,
     };
   },
   computed: {
@@ -115,6 +120,21 @@ export default {
 
       return this.resourceId || this.userId;
     },
+    shouldRenderResizable() {
+      return this.glFeatures.duoChatDynamicDimension;
+    },
+    dimensions() {
+      return {
+        width: this.width,
+        height: this.height,
+        top: this.top,
+        maxHeight: this.maxHeight,
+        maxWidth: this.maxWidth,
+        minWidth: this.minWidth,
+        minHeight: this.minHeight,
+        left: this.left,
+      };
+    },
     hasCommands() {
       return this.duoChatGlobalState.commands.length > 0;
     },
@@ -129,8 +149,34 @@ export default {
       },
     },
   },
+  mounted() {
+    this.setDimensions();
+    window.addEventListener('resize', this.onWindowResize);
+  },
+  beforeDestroy() {
+    // Remove the event listener when the component is destroyed
+    window.removeEventListener('resize', this.onWindowResize);
+  },
   methods: {
     ...mapActions(['addDuoChatMessage', 'setMessages', 'setLoading']),
+    setDimensions() {
+      this.updateDimensions();
+    },
+    updateDimensions(width, height) {
+      this.maxWidth = window.innerWidth - WIDTH_OFFSET;
+      this.maxHeight = window.innerHeight;
+
+      this.width = Math.min(width || this.width, this.maxWidth);
+      this.height = Math.min(height || this.height, this.maxHeight);
+      this.top = window.innerHeight - this.height;
+      this.left = window.innerWidth - this.width;
+    },
+    onChatResize(e) {
+      this.updateDimensions(e.width, e.height);
+    },
+    onWindowResize() {
+      this.updateDimensions();
+    },
     isClearOrResetMessage(question) {
       return [GENIE_CHAT_CLEAR_MESSAGE, GENIE_CHAT_RESET_MESSAGE].includes(question);
     },
@@ -277,9 +323,11 @@ export default {
         id="duo-chat"
         :slash-commands="aiSlashCommands"
         :title="$options.i18n.gitlabChat"
+        :dimensions="dimensions"
         :messages="messages"
         :error="error"
         :is-loading="loading"
+        :should-render-resizable="shouldRenderResizable"
         :predefined-prompts="$options.i18n.predefinedPrompts"
         :badge-type="null"
         :tool-name="toolName"
@@ -289,6 +337,7 @@ export default {
         @send-chat-prompt="onSendChatPrompt"
         @chat-hidden="onChatClose"
         @track-feedback="onTrackFeedback"
+        @chat-resize="onChatResize"
       />
     </div>
     <duo-chat-callout @callout-dismissed="onCalloutDismissed" />
diff --git a/ee/app/assets/javascripts/ai/tanuki_bot/constants.js b/ee/app/assets/javascripts/ai/tanuki_bot/constants.js
index 6ac2f3c551d964b679a73c3213d590ac0e6a602c..04f0753459b5a86effa18fd368371eca3929e3f9 100644
--- a/ee/app/assets/javascripts/ai/tanuki_bot/constants.js
+++ b/ee/app/assets/javascripts/ai/tanuki_bot/constants.js
@@ -27,3 +27,5 @@ export const ERROR_MESSAGE = s__(
 
 export const TANUKI_BOT_TRACKING_EVENT_NAME = 'ask_gitlab_chat';
 export const TANUKI_BOT_FEEDBACK_ISSUE_URL = 'https://gitlab.com/gitlab-org/gitlab/-/issues/408527';
+
+export const WIDTH_OFFSET = 10;
diff --git a/ee/app/assets/stylesheets/components/tanuki_bot.scss b/ee/app/assets/stylesheets/components/tanuki_bot.scss
index d9064c52c12b885cfb45584b99260c03063f99a2..06bbc143d4e5de7e93c701a911b0d7d7bcfe0940 100644
--- a/ee/app/assets/stylesheets/components/tanuki_bot.scss
+++ b/ee/app/assets/stylesheets/components/tanuki_bot.scss
@@ -1,3 +1,9 @@
+.duo-chat-resizable {
+  // important because we're overwritting styles on data-attr set by https://github.com/nikitasnv/vue-resizable
+  // to make sure we're having the correct scroll behaviour
+  position: fixed !important;
+}
+
 .duo-chat-container .duo-chat-drawer {
   z-index: $zindex-duo-chat;
 
diff --git a/ee/config/feature_flags/beta/duo_chat_dynamic_dimension.yml b/ee/config/feature_flags/beta/duo_chat_dynamic_dimension.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f26c462b212916f75de098f2ceae577bd3b1c05b
--- /dev/null
+++ b/ee/config/feature_flags/beta/duo_chat_dynamic_dimension.yml
@@ -0,0 +1,9 @@
+---
+name: duo_chat_dynamic_dimension
+feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/508981
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175019
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/508981
+milestone: '17.7'
+group: group::duo chat
+type: beta
+default_enabled: false
diff --git a/ee/lib/ee/gitlab/gon_helper.rb b/ee/lib/ee/gitlab/gon_helper.rb
index 05622f69fc0e8902fd50e24fa91dd36b40cae77c..11603533cec61ed6c8cc3e9dec96d72d8e5b154c 100644
--- a/ee/lib/ee/gitlab/gon_helper.rb
+++ b/ee/lib/ee/gitlab/gon_helper.rb
@@ -28,6 +28,7 @@ def add_gon_variables
         gon.billing_accounts_url             = ::Gitlab::Routing.url_helpers.subscription_portal_billing_accounts_url
         gon.payment_form_url                 = ::Gitlab::Routing.url_helpers.subscription_portal_payment_form_url
         gon.payment_validation_form_id       = ::Gitlab::SubscriptionPortal::PAYMENT_VALIDATION_FORM_ID
+        push_frontend_feature_flag(:duo_chat_dynamic_dimension)
         push_frontend_feature_flag(:advanced_context_resolver, current_user)
       end
 
diff --git a/ee/spec/frontend/ai/tanuki_bot/components/app_spec.js b/ee/spec/frontend/ai/tanuki_bot/components/app_spec.js
index 5d8f6ea298e8a70a0b2b68ba722a4076de487823..92b5bf1015462bcd0e604a9db03f4bc287c0bf2c 100644
--- a/ee/spec/frontend/ai/tanuki_bot/components/app_spec.js
+++ b/ee/spec/frontend/ai/tanuki_bot/components/app_spec.js
@@ -9,7 +9,7 @@ import TanukiBotChatApp from 'ee/ai/tanuki_bot/components/app.vue';
 import DuoChatCallout from 'ee/ai/components/global_callout/duo_chat_callout.vue';
 import TanukiBotSubscriptions from 'ee/ai/tanuki_bot/components/tanuki_bot_subscriptions.vue';
 import { GENIE_CHAT_RESET_MESSAGE, GENIE_CHAT_CLEAR_MESSAGE } from 'ee/ai/constants';
-import { TANUKI_BOT_TRACKING_EVENT_NAME } from 'ee/ai/tanuki_bot/constants';
+import { TANUKI_BOT_TRACKING_EVENT_NAME, WIDTH_OFFSET } from 'ee/ai/tanuki_bot/constants';
 import chatMutation from 'ee/ai/graphql/chat.mutation.graphql';
 import duoUserFeedbackMutation from 'ee/ai/graphql/duo_user_feedback.mutation.graphql';
 import getAiMessages from 'ee/ai/graphql/get_ai_messages.query.graphql';
@@ -82,6 +82,7 @@ describeSkipVue3(skipReason, () => {
   const createComponent = ({
     initialState = {},
     propsData = { userId: MOCK_USER_ID, resourceId: MOCK_RESOURCE_ID },
+    glFeatures = { duoChatDynamicDimension: false },
   } = {}) => {
     const store = new Vuex.Store({
       actions: actionSpies,
@@ -101,6 +102,9 @@ describeSkipVue3(skipReason, () => {
       store,
       apolloProvider,
       propsData,
+      provide: {
+        glFeatures,
+      },
     });
   };
 
@@ -611,4 +615,68 @@ describeSkipVue3(skipReason, () => {
       });
     });
   });
+
+  describe('Resizable Dimensions', () => {
+    beforeEach(() => {
+      duoChatGlobalState.isShown = true;
+      createComponent();
+    });
+
+    it('initializes dimensions correctly on mount', () => {
+      createComponent();
+      expect(wrapper.vm.width).toBe(400);
+      expect(wrapper.vm.height).toBe(window.innerHeight);
+      expect(wrapper.vm.maxWidth).toBe(window.innerWidth - WIDTH_OFFSET);
+      expect(wrapper.vm.maxHeight).toBe(window.innerHeight);
+    });
+
+    it('updates dimensions correctly when `chat-resize` event is emitted', async () => {
+      const newWidth = 600;
+      const newHeight = 500;
+      const chat = findDuoChat();
+      chat.vm.$emit('chat-resize', { width: newWidth, height: newHeight });
+      await nextTick();
+
+      expect(wrapper.vm.width).toBe(newWidth);
+      expect(wrapper.vm.height).toBe(newHeight);
+    });
+
+    it('ensures dimensions do not exceed maxWidth or maxHeight', async () => {
+      const newWidth = window.innerWidth + 100;
+      const newHeight = window.innerHeight + 100;
+      const chat = findDuoChat();
+
+      chat.vm.$emit('chat-resize', { width: newWidth, height: newHeight });
+      await nextTick();
+
+      expect(wrapper.vm.width).toBe(window.innerWidth - WIDTH_OFFSET);
+      expect(wrapper.vm.height).toBe(window.innerHeight);
+    });
+
+    it('updates dimensions when the window is resized', async () => {
+      createComponent();
+      window.innerWidth = 1200;
+      window.innerHeight = 800;
+
+      window.dispatchEvent(new Event('resize'));
+      await nextTick();
+
+      expect(wrapper.vm.maxWidth).toBe(1200 - WIDTH_OFFSET);
+      expect(wrapper.vm.maxHeight).toBe(800);
+    });
+
+    it('renders DuoChat with shouldRenderResizable=false when duoChatDynamicDimension flag is false', () => {
+      createComponent({ glFeatures: { duoChatDynamicDimension: false } });
+      const duoChat = findDuoChat();
+      expect(duoChat.exists()).toBe(true);
+      expect(duoChat.props('shouldRenderResizable')).toBe(false);
+    });
+
+    it('renders DuoChat with shouldRenderResizable=true when duoChatDynamicDimension flag is true', () => {
+      createComponent({ glFeatures: { duoChatDynamicDimension: true } });
+      const duoChat = findDuoChat();
+      expect(duoChat.exists()).toBe(true);
+      expect(duoChat.props('shouldRenderResizable')).toBe(true);
+    });
+  });
 });