Skip to content

Testimonial Section

The TestimonialSection and Testimonial components create beautiful customer testimonial displays with avatars, ratings, and quotes. Perfect for landing pages, marketing sites, and social proof sections.

Goal

By the end of this guide, you'll be able to create professional testimonial sections that build trust and credibility instantly.


Quick Start

Here's the simplest way to create a testimonial.

Live Preview
John Doe
CEO, Acme Corp

"This product changed my life! Highly recommended."

Testimonial(
    quote="This product changed my life! Highly recommended.",
    author="John Doe",
    role="CEO, Acme Corp",
    rating=5
)

Visual Examples & Use Cases

1. Complete Testimonial Section

Full section with multiple testimonials.

TestimonialSection(
    Testimonial(
        quote="FastStrap made building our app incredibly fast. We launched in weeks, not months!",
        author="Sarah Johnson",
        role="CTO, TechStart",
        avatar="/static/avatars/sarah.jpg",
        rating=5
    ),
    Testimonial(
        quote="The zero-JS philosophy is brilliant. Our pages load instantly and SEO is perfect.",
        author="Mike Chen",
        role="Lead Developer, WebCo",
        avatar="/static/avatars/mike.jpg",
        rating=5
    ),
    Testimonial(
        quote="Best FastHTML framework out there. Clean API, great docs, active community.",
        author="Emma Davis",
        role="Freelance Developer",
        avatar="/static/avatars/emma.jpg",
        rating=5
    ),
    title="What Our Customers Say",
    subtitle="Join thousands of developers building with FastStrap",
    columns=3
)

2. Without Avatars

Simple text-only testimonials.

Testimonial(
    quote="Simple, powerful, and exactly what I needed.",
    author="Alex Thompson",
    role="Indie Hacker"
)

3. Without Ratings

Testimonials without star ratings.

Testimonial(
    quote="The documentation is outstanding. I was productive in minutes.",
    author="Lisa Park",
    role="Software Engineer"
)

Practical Functionality

Dynamic Testimonials

Load testimonials from database.

def TestimonialsPage():
    # Get testimonials from database
    testimonials = db.query(Testimonial).filter(
        Testimonial.approved == True
    ).order_by(Testimonial.created_at.desc()).limit(6).all()

    return TestimonialSection(
        *[
            Testimonial(
                quote=t.quote,
                author=t.author_name,
                role=t.author_role,
                avatar=t.avatar_url,
                rating=t.rating
            )
            for t in testimonials
        ],
        title="Customer Reviews",
        subtitle=f"Rated {get_average_rating()}/5 by {len(testimonials)} customers"
    )

Rotating testimonials.

from faststrap.components.display import Carousel

Carousel(
    *[
        Testimonial(
            quote=t.quote,
            author=t.author,
            role=t.role,
            rating=t.rating
        )
        for t in featured_testimonials
    ],
    indicators=True,
    controls=True,
    auto_play=True,
    interval=5000
)

Video Testimonials

Embed video testimonials.

Card(
    Div(
        Video(
            src="/static/testimonials/john-doe.mp4",
            controls=True,
            cls="w-100"
        ),
        cls="ratio ratio-16x9"
    ),
    CardBody(
        H6("John Doe"),
        P("CEO, Acme Corp", cls="text-muted small"),
        P('"This product transformed our business."')
    )
)

Integration Patterns

In Landing Page

def LandingPage():
    return Container(
        # Hero section
        Hero(...),

        # Features
        FeatureGrid(...),

        # Testimonials
        TestimonialSection(
            *get_testimonials(),
            title="Loved by Developers",
            subtitle="See what our community is saying",
            cls="my-5 py-5 bg-light"
        ),

        # Pricing
        PricingGroup(...),

        # CTA
        CallToAction(...)
    )

With Trust Badges

Combine with company logos.

Section(
    Container(
        H2("Trusted by Industry Leaders", cls="text-center mb-4"),

        # Company logos
        Row(
            *[
                Col(
                    Img(src=f"/static/logos/{company}.png", cls="img-fluid"),
                    md=2
                )
                for company in ["google", "microsoft", "amazon", "apple", "meta", "netflix"]
            ],
            cls="justify-content-center align-items-center mb-5"
        ),

        # Testimonials
        TestimonialSection(
            *testimonials,
            title="What They're Saying"
        )
    ),
    cls="py-5"
)

Filterable Testimonials

Filter by category or rating.

def TestimonialsWithFilter():
    return Div(
        H2("Customer Stories"),

        # Filter buttons
        ButtonGroup(
            Button("All", hx_get="/testimonials?filter=all", hx_target="#testimonials"),
            Button("5 Stars", hx_get="/testimonials?filter=5", hx_target="#testimonials"),
            Button("Developers", hx_get="/testimonials?filter=dev", hx_target="#testimonials"),
            cls="mb-4"
        ),

        # Testimonials container
        Div(id="testimonials")
    )

@app.get("/testimonials")
def get_testimonials(filter: str = "all"):
    query = db.query(Testimonial)

    if filter == "5":
        query = query.filter(Testimonial.rating == 5)
    elif filter == "dev":
        query = query.filter(Testimonial.category == "developer")

    testimonials = query.all()

    return TestimonialSection(*[
        Testimonial(
            quote=t.quote,
            author=t.author,
            role=t.role,
            rating=t.rating
        )
        for t in testimonials
    ])

Parameter Reference

Testimonial

Parameter Type Default Description
quote str Required Testimonial quote text
author str Required Author name
role str \| None None Author role/title
avatar str \| None None Avatar image URL
rating int \| None None Star rating (1-5)
**kwargs Any - Additional HTML attributes

TestimonialSection

Parameter Type Default Description
*testimonials Any Required Testimonial components
title str \| None None Section title
subtitle str \| None None Section subtitle
columns int 3 Number of columns (1-4)
**kwargs Any - Additional HTML attributes

Best Practices

✅ Do This

# Use real customer quotes
Testimonial(
    quote="Specific, authentic feedback from real users",
    author="Real Name",
    role="Actual Title, Company"
)

# Include avatars for credibility
Testimonial(
    quote="...",
    author="...",
    avatar="/path/to/real/photo.jpg"  # Real photo
)

# Show ratings
Testimonial(
    quote="...",
    author="...",
    rating=5  # Helps build trust
)

# Organize in sections
TestimonialSection(
    *testimonials,
    title="What Our Customers Say",
    subtitle="Real feedback from real users"
)

❌ Don't Do This

# Don't use fake testimonials
Testimonial(
    quote="This is the best product ever!",  # Too generic
    author="Anonymous"  # Not credible
)

# Don't use stock photos
avatar="https://thispersondoesnotexist.com/..."  # Fake!

# Don't show only 5-star reviews
# Include variety for authenticity

# Don't overcrowd
TestimonialSection(*50_testimonials)  # Too many!

Complete Example

Full testimonials page with filtering.

from fasthtml.common import *
from faststrap import TestimonialSection, Testimonial, Container

@app.get("/testimonials")
def testimonials_page(category: str = "all"):
    # Get testimonials
    query = db.query(TestimonialModel)

    if category != "all":
        query = query.filter(TestimonialModel.category == category)

    testimonials = query.order_by(
        TestimonialModel.rating.desc()
    ).limit(12).all()

    # Calculate stats
    total = db.query(TestimonialModel).count()
    avg_rating = db.query(func.avg(TestimonialModel.rating)).scalar()

    return Container(
        # Header
        Div(
            H1("Customer Stories", cls="text-center"),
            P(
                f"{total} reviews • {avg_rating:.1f}/5 average rating",
                cls="text-center text-muted"
            ),
            cls="my-5"
        ),

        # Filter
        ButtonGroup(
            Button(
                "All",
                variant="primary" if category == "all" else "outline-primary",
                hx_get="/testimonials?category=all",
                hx_target="#testimonials-content"
            ),
            Button(
                "Developers",
                variant="primary" if category == "developer" else "outline-primary",
                hx_get="/testimonials?category=developer",
                hx_target="#testimonials-content"
            ),
            Button(
                "Businesses",
                variant="primary" if category == "business" else "outline-primary",
                hx_get="/testimonials?category=business",
                hx_target="#testimonials-content"
            ),
            cls="mb-4 d-flex justify-content-center"
        ),

        # Testimonials
        Div(
            TestimonialSection(
                *[
                    Testimonial(
                        quote=t.quote,
                        author=t.author_name,
                        role=t.author_role,
                        avatar=t.avatar_url,
                        rating=t.rating
                    )
                    for t in testimonials
                ],
                columns=3
            ),
            id="testimonials-content"
        )
    )

faststrap.components.patterns.testimonial

Testimonial Pattern Components.

Customer testimonial cards and sections for social proof.

Testimonial(quote, author, role=None, avatar=None, rating=None, **kwargs)

Single testimonial card with quote, author, and optional avatar/rating.

Parameters:

Name Type Description Default
quote str

Testimonial quote text

required
author str

Author name

required
role str | None

Author's role/title (optional)

None
avatar str | None

URL to author's avatar image (optional)

None
rating int | None

Star rating out of 5 (optional)

None
**kwargs Any

Additional HTML attributes

{}

Returns:

Type Description
Any

Card component with testimonial content

Example

Basic testimonial:

Testimonial( ... quote="This product changed my life!", ... author="John Doe", ... role="CEO, Acme Corp" ... )

With avatar and rating:

Testimonial( ... quote="Absolutely amazing experience.", ... author="Jane Smith", ... role="Designer", ... avatar="/static/avatars/jane.jpg", ... rating=5 ... )

Note

Marked as @beta - API may change in future releases.

Source code in src/faststrap/components/patterns/testimonial.py
@register(category="patterns")
@beta
def Testimonial(
    quote: str,
    author: str,
    role: str | None = None,
    avatar: str | None = None,
    rating: int | None = None,
    **kwargs: Any,
) -> Any:
    """Single testimonial card with quote, author, and optional avatar/rating.

    Args:
        quote: Testimonial quote text
        author: Author name
        role: Author's role/title (optional)
        avatar: URL to author's avatar image (optional)
        rating: Star rating out of 5 (optional)
        **kwargs: Additional HTML attributes

    Returns:
        Card component with testimonial content

    Example:
        Basic testimonial:
        >>> Testimonial(
        ...     quote="This product changed my life!",
        ...     author="John Doe",
        ...     role="CEO, Acme Corp"
        ... )

        With avatar and rating:
        >>> Testimonial(
        ...     quote="Absolutely amazing experience.",
        ...     author="Jane Smith",
        ...     role="Designer",
        ...     avatar="/static/avatars/jane.jpg",
        ...     rating=5
        ... )

    Note:
        Marked as @beta - API may change in future releases.
    """
    from ...utils.icons import Icon

    # Build avatar section
    avatar_section = None
    if avatar:
        avatar_section = Img(
            src=avatar,
            alt=author,
            cls="rounded-circle me-3",
            style="width: 48px; height: 48px; object-fit: cover;",
        )

    # Build rating stars
    rating_section = None
    if rating is not None:
        stars = []
        for i in range(5):
            star_icon = "star-fill" if i < rating else "star"
            stars.append(Icon(star_icon, cls="text-warning"))

        rating_section = Div(
            *stars,
            cls="mb-3",
        )

    # Build author info
    author_info = []
    author_info.append(Span(author, cls="fw-bold d-block"))
    if role:
        author_info.append(Span(role, cls="text-muted small"))

    author_section = Div(
        Div(
            avatar_section,
            Div(*author_info),
            cls="d-flex align-items-center",
        ),
        cls="mt-3",
    )

    # Build quote
    quote_section = Blockquote(
        P(f'"{quote}"', cls="mb-0 fst-italic"),
        cls="mb-3",
    )

    # Build card content
    card_content = []
    if rating_section:
        card_content.append(rating_section)
    card_content.append(quote_section)
    card_content.append(author_section)

    # Build classes
    user_cls = kwargs.pop("cls", "")
    all_classes = merge_classes("h-100", user_cls)

    return Card(
        *card_content,
        cls=all_classes,
        **kwargs,
    )
TestimonialSection(*testimonials, title='What Our Customers Say', subtitle=None, columns=3, **kwargs)

Section displaying multiple testimonials in a grid.

Parameters:

Name Type Description Default
*testimonials Any

Testimonial components to display

()
title str

Section title

'What Our Customers Say'
subtitle str | None

Optional subtitle

None
columns int

Number of columns in grid (default: 3)

3
**kwargs Any

Additional HTML attributes

{}

Returns:

Type Description
Div

Div with testimonials in responsive grid

Example

Basic testimonial section:

TestimonialSection( ... Testimonial( ... quote="Great product!", ... author="Alice", ... role="Developer" ... ), ... Testimonial( ... quote="Highly recommend!", ... author="Bob", ... role="Designer" ... ), ... Testimonial( ... quote="Best decision ever!", ... author="Carol", ... role="Manager" ... ) ... )

Custom title and 2 columns:

TestimonialSection( ... *testimonials, ... title="Success Stories", ... subtitle="Hear from our happy customers", ... columns=2 ... )

Note

Marked as @beta - API may change in future releases.

Source code in src/faststrap/components/patterns/testimonial.py
@register(category="patterns")
@beta
def TestimonialSection(
    *testimonials: Any,
    title: str = "What Our Customers Say",
    subtitle: str | None = None,
    columns: int = 3,
    **kwargs: Any,
) -> Div:
    """Section displaying multiple testimonials in a grid.

    Args:
        *testimonials: Testimonial components to display
        title: Section title
        subtitle: Optional subtitle
        columns: Number of columns in grid (default: 3)
        **kwargs: Additional HTML attributes

    Returns:
        Div with testimonials in responsive grid

    Example:
        Basic testimonial section:
        >>> TestimonialSection(
        ...     Testimonial(
        ...         quote="Great product!",
        ...         author="Alice",
        ...         role="Developer"
        ...     ),
        ...     Testimonial(
        ...         quote="Highly recommend!",
        ...         author="Bob",
        ...         role="Designer"
        ...     ),
        ...     Testimonial(
        ...         quote="Best decision ever!",
        ...         author="Carol",
        ...         role="Manager"
        ...     )
        ... )

        Custom title and 2 columns:
        >>> TestimonialSection(
        ...     *testimonials,
        ...     title="Success Stories",
        ...     subtitle="Hear from our happy customers",
        ...     columns=2
        ... )

    Note:
        Marked as @beta - API may change in future releases.
    """
    from ..layout.grid import Col, Container, Row

    # Build header
    header_content = [
        Div(
            title,
            cls="h2 text-center mb-2",
        )
    ]
    if subtitle:
        header_content.append(P(subtitle, cls="text-center text-muted mb-5"))

    header = Div(*header_content)

    # Build testimonial grid
    col_size = 12 // columns
    testimonial_cols = []
    for testimonial in testimonials:
        testimonial_cols.append(
            Col(
                testimonial,
                md=col_size,
                cls="mb-4",
            )
        )

    testimonial_grid = Row(*testimonial_cols)

    # Build classes
    base_classes = ["py-5"]
    user_cls = kwargs.pop("cls", "")
    all_classes = merge_classes(" ".join(base_classes), user_cls)

    # Build attributes
    attrs: dict[str, Any] = {"cls": all_classes}
    attrs.update(convert_attrs(kwargs))

    return Div(
        Container(
            header,
            testimonial_grid,
        ),
        **attrs,
    )