How to render CSS/JS/Images along with HTML file in FastAPI?

Question:

I am facing an issue while rendering the HTMl file in FastAPI.

main.py file

static_dir = os.path.join(os.path.dirname(__file__), "static")
app.mount("/",StaticFiles(directory=static_dir, html=True),name="static")

@app.get("/")
async def index():
    return FileResponse('index.html', media_type='text/html')

While running the above file using uvicorn I am able to render the HTML file at http://127.0.0.1:8765/, but the static files, such as css, js and images, are not getting rendered.

index.html: some code of HTML File (which is build from Angular JS)

<link rel="stylesheet" href="styles.87afad25367d1df4.css" media="print" onload="this.media='all'"><noscript>
<link rel="stylesheet" href="styles.87afad25367d1df4.css"></noscript></head>
<body class="cui">
  test
  <app-root></app-root>
<script src="runtime.7f95ee6540776f88.js" type="module"></script>
<script src="polyfills.a246e584d5c017d7.js" type="module"></script>
<script src="main.4f51d0f81827a3db.js" type="module"></script>

</body></html>

File Structure:

modulename
  - static
       - index.html
       - styles.87afad25367d1df4.css
       - runtime.7f95ee6540776f88.js
       - polyfills.a246e584d5c017d7.js
       - main.4f51d0f81827a3db.js
       
  - main.py 
  - __init__.py

When I open the browser console it show like below:
enter image description here

The CSS/js should be render without static included in it e.g. http://127.0.0.1:8765/styles.87afad25367d1df4.css but it run on browser it loads from http://127.0.0.1:8765/static/styles.87afad25367d1df4.css.

I am not sure how to fix this any help will be appreciated.

Update: Adding below code to explain it better

main.py

import uvicorn
import os
import webbrowser
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse

app = FastAPI(
    title="UI",
    description="This is to test",
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

static_dir = os.path.join(os.path.dirname(__file__), "static")
app.mount("/",StaticFiles(directory=static_dir, html=True),name="static")

def start_server():
    # print('Starting Server...')       

    uvicorn.run(
        "ui.main:app",
        host="0.0.0.0",
        port=8765,
        log_level="debug",
        reload=True,
    )
    # webbrowser.open("http://127.0.0.1:8765/")

if __name__ == "__main__":
    start_server()

Running this file as package/module in test.py file:

from ui import main

if __name__ == "__main__":
    main.start_server()

index.html:

<!DOCTYPE html><html lang="en">
  <head>
  <meta charset="utf-8">
  <title>WingmanUi</title>
  <base href="static/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  

<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles.87afad25367d1df4.css" media="print" onload="this.media='all'">
</head>
<body>
  This is to test  
<script src="runtime.7f95ee6540776f88.js" type="module"></script>
<script src="polyfills.a246e584d5c017d7.js" type="module"></script>
<script src="main.4f51d0f81827a3db.js" type="module"></script>

</body>
</html>

File structure:

ui
  - static
       - index.html
       - styles.87afad25367d1df4.css
       - runtime.7f95ee6540776f88.js
       - polyfills.a246e584d5c017d7.js
       - main.4f51d0f81827a3db.js
       
  - main.py 
  - __init__.py
Asked By: Rakesh Shetty

||

Answers:

First, when using StaticFiles with the html flag set to True, in order to serve static files (for dynamic webpages, see Templates instead), for instance:

from fastapi import FastAPI
from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount('/static', StaticFiles(directory='static', html=True), name='static')

you don’t have to define an endpoint to serve the index page, as html=True means running your app in HTML mode; thus, FastAPI/Starlette automatically loads index.html—see Starlette documentation on StaticFiles. Also, in case you need to have additional endpoints, note that the order that endpoints are defined in your application matters.

Second, since you have mounted your StaticFiles instance, specifying the directory='static', then your static files are expected to be served from that directory. Hence, all you have to do is to move all your static files, along with the HTML file, inside the static directory. You should then be able to serve your static files as follows (given that you have mounted StaticFiles instance to /, e.g., app.mount('/', ...):

<script src="someScript.js"></script>

or, if StaticFiles instance is mounted to /static, e.g., app.mount('/static', ...) (see this answer as well):

<script src="static/someScript.js"></script>

In your case, since you have mounted StaticFiles instance to /, the reason for your static files still being loaded with the static prefix, e.g., http://127.0.0.1:8000/static/someScript.js is simply because you have added a <base> URL in your HTML document to use for all relative URLs. Hence, you should remove the following line from your HTML file:

<base href="static/">
Answered By: Chris
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.