Question: I have a FastAPI endpoint that is generating PIL images. I want to then send the resulting image as a stream to a Jinja2 TemplateResponse. This is a simplified version of what I am doing: import io from PIL import Image @api.get("/test_image", status_code=status.HTTP_200_OK) def test_image(request: Request): '''test displaying an image from a stream. ''' test_img = Image.new('RGBA', (300,300), (0, 255, 0, 0)) # I've tried with and without this: test_img = test_img.convert("RGB") test_img = test_img.tobytes() base64_encoded_image = base64.b64encode(test_img).decode("utf-8") return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})  With this simple html: <html> <head> <title>Display Uploaded Image</title> </head> <body> <h1>My Image<h1> <img src="data:image/jpeg;base64,{{ myImage | safe }}"> </body> </html>  I've been working from these answers and have tried multiple permutations of these: How to display uploaded image in HTML page using FastAPI & Jinja2? How to convert PIL Image.image object to base64 string? How can I display PIL image to html with render_template flask? This seems like it ought to be very simple but all I get is the html icon for an image that didn't render. What am I doing wrong? Thank you. I used Mark Setchell's answer, which clearly shows what I was doing wrong, but still am not getting an image in html. My FastAPI is: @api.get("/test_image", status_code=status.HTTP_200_OK) def test_image(request: Request): # Create image im = Image.new('RGB',(1000,1000),'red') im.save('red.png') print(im.tobytes()) # Create buffer buffer = io.BytesIO() # Tell PIL to save as PNG into buffer im.save(buffer, 'PNG') # get the PNG-encoded image from buffer PNG = buffer.getvalue() print() print(PNG) base64_encoded_image = base64.b64encode(PNG) return templates.TemplateResponse("display_image.html", {"request": request, "myImage": base64_encoded_image})  and my html: <html> <head> <title>Display Uploaded Image</title> </head> <body> <h1>My Image 3<h1> <img src="data:image/png;base64,{{ myImage | safe }}"> </body> </html>  When I run, if I generate a 1x1 image I get the exact printouts in Mark's answer. If I run this version, with 1000x1000 image, it saves a red.png that I can open and see. But in the end, the html page has the heading and the icon for no image rendered. I'm clearly doing something wrong now in how I send to html.

Answer: There are a couple of issues here. I'll make a new section for each to keep it clearly divided up.  If you want to send a base64-encoded PNG, you need to change your HTML to: <img src="data:image/png;base64,{{ myImage | safe }}">   If you create an image of a single red pixel like this: im = Image.new('RGB',(1,1),'red') print(im.tobytes())  you'll get: b'\xff\x00\x00'  That is not a PNG-encoded image, how could it be - you haven't told PIL that you want a PNG, or a JPEG, or a TIFF, so it cannot know. It is just giving you the 3 raw RGB pixels as bytes #ff0000. If you save that image to disk as a PNG and dump it you will get: im.save('red.png')  Then dump it: xxd red.png 00000000: 8950 4e47 0d0a 1a0a 0000 000d 4948 4452 .PNG........IHDR 00000010: 0000 0001 0000 0001 0802 0000 0090 7753 ..............wS 00000020: de00 0000 0c49 4441 5478 9c63 f8cf c000 .....IDATx.c.... 00000030: 0003 0101 00c9 fe92 ef00 0000 0049 454e .............IEN 00000040: 44ae 4260 82 D.B`.  You can now see the PNG signature at the start. So we need to create that same thing, but just in memory without bothering the disk: import io import base64 from PIL import image # Create image im = Image.new('RGB',(1,1),'red') # Create buffer buffer = io.BytesIO() # Tell PIL to save as PNG into buffer im.save(buffer, 'PNG')  Now we can get the PNG-encoded image from the buffer: PNG = buffer.getvalue()  And if we print it, it will look suspiciously identical to the PNG on disk: b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDATx\x9cc\xf8\xcf\xc0\x00\x00\x03\x01\x01\x00\xc9\xfe\x92\xef\x00\x00\x00\x00IEND\xaeB`\x82'  Now you can base64-encode it and send it: base64_encoded_image = base64.b64encode(PNG)   Note: I only made 1x1 for demonstration purposes so I could show you the whole file. Make it bigger than 1x1 when you test, or you'll never see it 😀