Of course! The thumbnail() method is a highly useful function in Python's Pillow library (a fork of PIL) for creating smaller versions of images. It's designed to be efficient because it modifies the image in-place, meaning it changes the original image object directly.

Here’s a complete guide covering what it does, how to use it, and best practices.
What is thumbnail()?
The thumbnail() method takes a tuple representing the maximum width and height for the thumbnail and resizes the image to fit within that box, while maintaining the original aspect ratio.
Key Characteristics:
- In-Place Operation: It does not return a new image object. Instead, it modifies the image object you call it on.
- Maintains Aspect Ratio: It will never stretch or squish your image. The resulting thumbnail will have the same proportions as the original.
- Fits Inside the Box: The thumbnail's dimensions will be less than or equal to the size you specify. If the original image is already smaller than the target size, it will not be enlarged.
- Efficient: Because it modifies the image in-place, it's very memory-efficient.
Prerequisites: Installing Pillow
If you don't have Pillow installed, open your terminal or command prompt and run:

pip install Pillow
Basic Usage
Let's start with a simple example. Imagine you have an image named my_photo.jpg.
from PIL import Image
# Open an image file
try:
img = Image.open('my_photo.jpg')
# Print the original size
print(f"Original size: {img.size}")
# Define the size for the thumbnail (width, height)
# We want a thumbnail that is no larger than 300x300 pixels
thumbnail_size = (300, 300)
# Create the thumbnail
img.thumbnail(thumbnail_size)
# Print the new size
print(f"New size: {img.size}")
# Save the modified image (the original is now the thumbnail)
img.save('my_photo_thumbnail.jpg')
print("Thumbnail created successfully!")
except FileNotFoundError:
print("Error: 'my_photo.jpg' not found. Please make sure the image is in the same directory.")
except Exception as e:
print(f"An error occurred: {e}")
What Happens in This Example?
Image.open('my_photo.jpg')loads your image into anImageobject.img.sizegives you a tuple of the original dimensions, e.g.,(1200, 800).img.thumbnail((300, 300))resizes the image. Since the original is1200x800, the new size will be calculated to fit inside the300x300box while keeping the aspect ratio.- The aspect ratio is
1200 / 800 = 1.5. - To fit
300x300, the height will be the limiting factor. - New height =
300. - New width =
300 * 1.5 = 450. - Wait,
450is greater than300! This logic is slightly off. Let's correct it. - Correct logic: The scale factor is the minimum of
(300/1200, 300/800), which ismin(0.25, 0.375). So, the scale factor is25. - New width =
1200 * 0.25 = 300. - New height =
800 * 0.25 = 200. - So, the final size will be
(300, 200).
- The aspect ratio is
img.save('my_photo_thumbnail.jpg')saves the now-resized image. The originalmy_photo.jpgfile is not changed unless you overwrite it. In this code, we create a new file.
Understanding the Aspect Ratio Logic
This is the most important concept to grasp. The thumbnail method creates a "bounding box" and scales the image down to fit perfectly inside it without any cropping.
Let's use different examples:
| Original Size | Target Size (thumbnail_size) |
Resulting Thumbnail Size | Why? |
|---|---|---|---|
(1200, 800) |
(300, 300) |
(300, 200) |
Width is the limiting dimension. |
(800, 1200) |
(300, 300) |
(200, 300) |
Height is the limiting dimension. |
(500, 500) |
(300, 300) |
(300, 300) |
The image fits perfectly inside the box. |
(200, 100) |
(300, 300) |
(200, 100) |
The image is already smaller than the target, so it's not enlarged. |
Common Pitfalls and Best Practices
Pitfall 1: Forgetting It's In-Place
A common mistake is trying to assign the result of thumbnail() to a new variable.

# INCORRECT
img = Image.open('my_photo.jpg')
thumb = img.thumbnail((300, 300)) # thumb will be None!
print(thumb) # Output: None
print(img.size) # The original image is now modified, which might not be what you wanted.
Best Practice: If you need to keep the original image, make a copy before creating the thumbnail.
from PIL import Image
img = Image.open('my_photo.jpg')
# Keep a copy of the original image
original_img = img.copy()
# Now, create a thumbnail from the original
img.thumbnail((300, 300))
print(f"Original image size: {original_img.size}")
print(f"Thumbnail image size: {img.size}")
Pitfall 2: Quality Loss
Simply shrinking an image can make it look blurry or pixelated. For better results, you can use a high-quality resampling filter. The default filter is Image.NEAREST, which is fast but not the highest quality.
The thumbnail() method accepts an optional resample argument. For the best quality, use Image.LANCZOS (or Image.Resampling.LANCZOS in newer Pillow versions).
from PIL import Image
img = Image.open('my_photo.jpg')
img_copy = img.copy()
# Create a high-quality thumbnail
img_copy.thumbnail((300, 300), resample=Image.LANCZOS)
img_copy.save('high_quality_thumbnail.jpg')
print("High-quality thumbnail created.")
Best Practice: Handling Different File Formats
Some image formats, like JPEG, are "lossy." Every time you save a JPEG, you lose some quality. If you are creating and saving multiple times, it's better to use a "lossless" format like PNG for intermediate steps.
# Save as PNG for maximum quality in an intermediate step
img_copy.save('thumbnail_intermediate.png')
# If you absolutely need a final JPEG, do it only once at the end
img_copy.save('final_thumbnail.jpg', quality=95) # quality=95 is high quality
Complete Example: Batch Processing
Here's how you could use thumbnail() to process all images in a folder.
import os
from PIL import Image
def create_thumbnails(input_folder, output_folder, size=(300, 300)):
"""
Creates thumbnails for all images in an input folder and saves them to an output folder.
"""
# Create the output folder if it doesn't exist
if not os.path.exists(output_folder):
os.makedirs(output_folder)
print(f"Created output folder: {output_folder}")
# Supported image extensions
valid_extensions = ('.png', '.jpg', '.jpeg', '.gif', '.bmp')
print(f"Processing images from: {input_folder}")
# Loop through all files in the input folder
for filename in os.listdir(input_folder):
# Check if the file is an image
if filename.lower().endswith(valid_extensions):
try:
# Construct full file path
input_path = os.path.join(input_folder, filename)
# Open the image
with Image.open(input_path) as img:
# Create a thumbnail (in-place operation)
img.thumbnail(size, resample=Image.LANCZOS)
# Construct output path
# e.g., 'my_photo.jpg' -> 'my_photo_thumb.jpg'
base, ext = os.path.splitext(filename)
output_filename = f"{base}_thumb{ext}"
output_path = os.path.join(output_folder, output_filename)
# Save the thumbnail
img.save(output_path)
print(f"Created thumbnail: {output_path}")
except Exception as e:
print(f"Could not process {filename}. Error: {e}")
# --- Run the script ---
if __name__ == "__main__":
# Replace with your folder paths
source_dir = 'source_images'
destination_dir = 'thumbnails'
create_thumbnails(source_dir, destination_dir)
To run this, create a folder named source_images in the same directory as your script and put some images in it. After running, a thumbnails folder will be created with the resized images.
