diff --git a/theme/src/ts/external-links.ts b/theme/src/ts/external-links.ts
new file mode 100644
index 000000000000..d7f139d788bb
--- /dev/null
+++ b/theme/src/ts/external-links.ts
@@ -0,0 +1,57 @@
+/**
+ * Adds target="_blank" to all external links to make them open in a new tab.
+ * Internal links (links to the same domain) remain unchanged.
+ */
+
+(function() {
+    // Function to process all links on the page
+    function processExternalLinks() {
+        // Get the current domain (without protocol, www, or trailing slash)
+        const currentDomain = window.location.hostname.replace(/^www\./, '');
+        
+        // Select all links in the document
+        const links = document.querySelectorAll('a[href]');
+        
+        // Process each link
+        links.forEach(link => {
+            const href = link.getAttribute('href');
+            
+            // Skip links without href, anchor links, or javascript: links
+            if (!href || href.startsWith('#') || href.startsWith('javascript:')) {
+                return;
+            }
+            
+            try {
+                // Try to parse the URL (this will throw for relative URLs)
+                const url = new URL(href, window.location.origin);
+                const linkDomain = url.hostname.replace(/^www\./, '');
+                
+                // If the domain is different from the current domain, it's external
+                if (linkDomain !== currentDomain) {
+                    // Add target="_blank" and rel="noopener" (for security)
+                    link.setAttribute('target', '_blank');
+                    
+                    // Add rel="noopener noreferrer" for security
+                    const relAttr = link.getAttribute('rel') || '';
+                    if (!relAttr.includes('noopener')) {
+                        const newRel = relAttr ? `${relAttr} noopener` : 'noopener';
+                        link.setAttribute('rel', newRel);
+                    }
+                }
+            } catch (e) {
+                // If URL parsing fails, it's likely a relative URL (internal link)
+                return;
+            }
+        });
+    }
+
+    // Execute when DOM is fully loaded
+    if (document.readyState === 'loading') {
+        document.addEventListener('DOMContentLoaded', processExternalLinks);
+    } else {
+        processExternalLinks();
+    }
+
+    // Also process links after any potential dynamic content updates
+    window.addEventListener('load', processExternalLinks);
+})();
\ No newline at end of file
diff --git a/theme/src/ts/main.ts b/theme/src/ts/main.ts
index 523b4b5820ad..c7d677db0b6c 100644
--- a/theme/src/ts/main.ts
+++ b/theme/src/ts/main.ts
@@ -24,6 +24,7 @@ import "./docs-main";
 import "./redirects";
 import "./algolia/autocomplete";
 import "./terraform-compare";
+import "./external-links";
 
 // Register all Stencil components.
 defineCustomElements();