NeuroAgent

Where to Put Script Tags in HTML: Complete Guide

Learn optimal script tag placement for better performance. Discover traditional vs modern approaches, async/defer attributes, and best practices for different use cases.

Question

Where should I put <script> tags in HTML markup?

When embedding JavaScript in an HTML document, where is the proper place to put the <script> tags and included JavaScript? I seem to recall that you are not supposed to place these in the <head> section, but placing at the beginning of the <body> section is bad, too, since the JavaScript will have to be parsed before the page is rendered completely. This seems to leave the end of the <body> section as a logical place for <script> tags.

So, where is the right place to put the <script> tags?

(Note: This question references a related discussion about moving JavaScript function calls from <a> tags to <script> tags. I’m specifically using jQuery, but more general answers are also appropriate.)

NeuroAgent

The best placement for <script> tags in HTML depends on several factors including performance, functionality needs, and modern web development practices. While traditionally placing scripts at the end of the <body> was recommended to prevent blocking page rendering, modern approaches offer more flexibility and performance optimization opportunities.

Contents

Traditional Script Placement Approaches

End of the <body> Section

The traditional approach places <script> tags just before the closing </body> tag. This method prevents JavaScript from blocking the initial page rendering because the browser first renders all HTML content before executing the scripts.

html
<!DOCTYPE html>
<html>
<head>
    <title>My Page</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <h1>Welcome to my page</h1>
    <p>This content loads quickly.</p>
    
    <!-- Scripts at the end of body -->
    <script src="jquery.js"></script>
    <script src="app.js"></script>
</body>
</html>

Advantages:

  • Fast content rendering
  • No render-blocking behavior
  • DOM elements are available when scripts execute

Beginning of the <body> Section

Placing scripts at the beginning of the <body> is generally discouraged because it blocks rendering. The browser must download and parse JavaScript before continuing to render the rest of the page content.

html
<body>
    <!-- Bad practice - blocks rendering -->
    <script src="jquery.js"></script>
    <script src="app.js"></script>
    
    <h1>Welcome to my page</h1>
    <p>This content won't appear until scripts load.</p>
</body>

In the <head> Section

While placing scripts in the <head> section blocks rendering, there are cases where it’s necessary, especially for libraries that need to load before any DOM manipulation occurs.

html
<head>
    <title>My Page</title>
    <script src="jquery.js"></script> <!-- May be necessary here -->
</head>

Modern Script Loading Strategies

Async and Defer Attributes

Modern HTML provides the async and defer attributes to control script loading behavior without moving scripts to the end of the document.

Async Attribute:

html
<script src="analytics.js" async></script>
  • Downloads without blocking parsing
  • Executes as soon as download completes
  • Not guaranteed to execute in order
  • Best for independent scripts like analytics

Defer Attribute:

html
<script src="jquery.js" defer></script>
<script src="app.js" defer></script>
  • Downloads without blocking parsing
  • Executes after document parsing completes
  • Maintains execution order
  • Best for dependent scripts

Module Scripts

ES6 modules can use the type="module" attribute, which automatically applies deferred loading:

html
<script type="module" src="main.js"></script>

Performance Considerations

Render-Blocking Behavior

  • Without async/defer: Scripts are render-blocking regardless of placement
  • With async/defer: Scripts download asynchronously but may still block rendering under certain conditions
  • Critical rendering path: Understanding the browser’s rendering process helps optimize script placement

Load Impact on Largest Contentful Paint (LCP)

  • Scripts that block rendering can significantly impact LCP scores
  • Non-critical scripts should be loaded after LCP elements
  • Consider using loading="lazy" for non-essential scripts
html
<script src="non-critical-feature.js" loading="lazy"></script>

jQuery-Specific Recommendations

Loading jQuery Before Dependent Scripts

When using jQuery, ensure it loads before any dependent scripts:

html
<!-- Option 1: With defer -->
<script src="jquery.js" defer></script>
<script src="my-jquery-code.js" defer></script>

<!-- Option 2: At end of body -->
<body>
    <!-- Page content -->
    <script src="jquery.js"></script>
    <script src="my-jquery-code.js"></script>
</body>

jQuery CDN Placement

jQuery is often loaded from CDNs. Modern CDNs support features like preconnect and DNS-prefetch:

html
<head>
    <link rel="preconnect" href="https://code.jquery.com">
    <link rel="dns-prefetch" href="https://code.jquery.com">
    <script src="https://code.jquery.com/jquery-3.7.1.min.js" 
            integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" 
            crossorigin="anonymous"></script>
</head>

Best Practices by Use Case

Critical Scripts

  • Use inline in <head> with async for critical functionality
  • Consider placing critical JavaScript in the <head> with defer
html
<head>
    <script>
        // Critical inline script
        function criticalFunction() {
            // Essential functionality
        }
    </script>
    <script src="critical-library.js" defer></script>
</head>

Third-Party Scripts

  • Use async for independent third-party scripts (analytics, ads)
  • Use defer for scripts that depend on DOM or other scripts

Application Scripts

  • Use defer for most application JavaScript
  • Place at end of <body> if not using async/defer attributes

Advanced Optimization Techniques

Resource Hints

Implement resource hints to optimize script loading:

html
<head>
    <!-- Preconnect to CDN -->
    <link rel="preconnect" href="https://cdn.example.com">
    
    <!-- DNS prefetch for domain -->
    <link rel="dns-prefetch" href="https://cdn.example.com">
    
    <!-- Preload critical scripts -->
    <link rel="preload" href="critical.js" as="script">
</head>

Dynamic Script Loading

Load scripts dynamically when needed:

javascript
function loadScript(src, callback) {
    const script = document.createElement('script');
    script.src = src;
    script.onload = callback;
    document.body.appendChild(script);
}

// Load non-critical script after page interaction
document.addEventListener('click', () => {
    loadScript('interactive-feature.js', () => {
        // Initialize feature
    });
});

Script Bundling and Code Splitting

  • Bundle related scripts together
  • Implement code splitting for large applications
  • Use modern bundlers like Webpack, Rollup, or Vite

Performance Monitoring

Monitor script loading performance:

html
<script>
    // Performance monitoring
    window.addEventListener('load', () => {
        const loadTime = performance.now();
        console.log('Page loaded in:', loadTime, 'ms');
    });
</script>

Conclusion

The optimal placement of <script> tags in HTML has evolved significantly with modern web development practices. While placing scripts at the end of the <body> remains a viable approach, the async and defer attributes provide more flexibility and better performance. For jQuery applications, ensure the library loads before dependent scripts, and consider using modern loading strategies. The best approach depends on your specific use case, performance requirements, and the criticality of your scripts. Always test and measure the impact of your script placement decisions on real-world performance metrics.