<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bharadwaz Kari</title>
    <description>The latest articles on DEV Community by Bharadwaz Kari (@zendizmo).</description>
    <link>https://dev.to/zendizmo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F740761%2F990a60c1-5ec4-4e69-b0e5-ed241e84868a.jpeg</url>
      <title>DEV Community: Bharadwaz Kari</title>
      <link>https://dev.to/zendizmo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zendizmo"/>
    <language>en</language>
    <item>
      <title>Building a FastAPI Application with Kiro</title>
      <dc:creator>Bharadwaz Kari</dc:creator>
      <pubDate>Wed, 18 Mar 2026 19:00:40 +0000</pubDate>
      <link>https://dev.to/zendizmo/building-a-fastapi-application-with-kiro-1mcg</link>
      <guid>https://dev.to/zendizmo/building-a-fastapi-application-with-kiro-1mcg</guid>
      <description>&lt;p&gt;Scaffolding a new API project involves a significant amount of repetitive setup — defining models, writing route handlers, configuring middleware, and wiring up error handling. These are well-understood patterns, but they still take time to implement correctly every time you start a new service. &lt;/p&gt;

&lt;p&gt;In this walkthrough, I use Kiro to scaffold a complete FastAPI task management API from a single natural language description. The result is a production-ready project structure with Pydantic validation, dependency injection, request logging, and auto-generated Swagger documentation. &lt;/p&gt;

&lt;p&gt;What I Asked Kiro &lt;/p&gt;

&lt;p&gt;I described the API I wanted in plain English: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Build a FastAPI task management API with CRUD endpoints, Pydantic models for validation, dependency injection, request logging middleware, and global error handling. Use an in-memory database with seed data. Include status and priority filtering on the list endpoint."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8wzhhc7j1zkvl4xybk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8wzhhc7j1zkvl4xybk4.png" alt=" " width="800" height="1400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro generates code based on the intent of your prompt, not from a fixed template. If you use the same prompt, you will get a structurally similar result — the same endpoints, the same separation of concerns, the same patterns — but the exact variable names, docstrings, seed data, or minor implementation details may differ between runs. Think of it like asking two senior developers to build the same spec: the architecture will align, but the code will not be character-for-character identical. &lt;/p&gt;

&lt;p&gt;The screenshots and code snippets in this walkthrough reflect one specific generation. Your output will follow the same patterns and conventions, but may not match line-for-line. &lt;/p&gt;

&lt;p&gt;The Generated Project Structure &lt;/p&gt;

&lt;p&gt;Kiro produced a clean, modular project layout: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;fastapi-task-app/ &lt;br&gt;
├── main.py          # Application setup, middleware, error handlers &lt;br&gt;
├── models.py         # Pydantic request/response schemas &lt;br&gt;
├── database.py       # In-memory data store with seed data &lt;br&gt;
├── routes.py         # CRUD endpoint handlers &lt;br&gt;
├── dependencies.py   # Dependency injection configuration &lt;br&gt;
└── requirements.txt &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Each concern is separated into its own module — models, routes, database, and dependencies are all independent. Kiro went with a flat layout, keeping all source files at the top level. This is a common pattern for smaller FastAPI services where a package subdirectory would add unnecessary nesting. &lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;SCREENSHOT: Kiro file explorer showing the generated project tree&lt;/em&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpxbtfs1n2xccpxq3304.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpxbtfs1n2xccpxq3304.png" alt=" " width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro generated three distinct schemas — one for creation, one for updates, and one for responses — along with enums for constrained fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Status(str, Enum): 
    todo = "todo" 
    in_progress = "in_progress" 
    done = "done" 

class Priority(str, Enum): 
    low = "low" 
    medium = "medium" 
    high = "high" 

class TaskCreate(BaseModel): 
    title: str = Field(..., min_length=1, max_length=200) 
    description: str = Field(default="", max_length=1000) 
    status: Status = Status.todo 
    priority: Priority = Priority.medium 

class TaskUpdate(BaseModel): 
    title: str | None = Field(default=None, min_length=1, max_length=200) 
    description: str | None = Field(default=None, max_length=1000) 
    status: Status | None = None 
    priority: Priority | None = None 

class Task(BaseModel): 
    id: int 
    title: str 
    description: str 
    status: Status 
    priority: Priority 
    created_at: datetime 
    updated_at: datetime 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TaskUpdate schema uses all optional fields with the modern str | None union syntax, enabling partial updates where only the provided fields are modified. The Field definitions include constraints that automatically appear in the generated API documentation. &lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Kiro editor showing models.py with the Pydantic schemas]&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4p611jz7ue9mqaheriob.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4p611jz7ue9mqaheriob.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro configured a dependency injection pattern for the database layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import database 

def get_db(): 
    """Dependency that yields the database module.""" 
    return database 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern decouples the route handlers from the data layer. When you move to a real database, you change what get_db() returns — the routes remain untouched. It also makes unit testing straightforward, since you can inject a mock. &lt;/p&gt;

&lt;p&gt;CRUD Endpoints &lt;/p&gt;

&lt;p&gt;The generated routes follow REST conventions with proper HTTP status codes, query parameter filtering, and consistent error handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.get("", response_model=list[Task]) 
def list_tasks( 
    status: Status | None = Query(default=None, description="Filter by status"), 
    priority: Priority | None = Query(default=None, description="Filter by priority"), 
    db: db_module = Depends(get_db), 
): 
    return db.get_all(status=status, priority=priority) 

@router.get("/{task_id}", response_model=Task) 
def get_task(task_id: int, db: db_module = Depends(get_db)): 
    task = db.get_by_id(task_id) 
    if not task: 
        raise HTTPException(status_code=404, detail="Task not found") 
    return task 

@router.post("", response_model=Task, status_code=201) 
def create_task(body: TaskCreate, db: db_module = Depends(get_db)): 
    now = datetime.now(timezone.utc) 
    task = Task( 
        id=db.next_id(), 
        title=body.title, 
        description=body.description, 
        status=body.status, 
        priority=body.priority, 
        created_at=now, 
        updated_at=now, 
    ) 
    return db.create(task) 

@router.patch("/{task_id}", response_model=Task) 
def update_task(task_id: int, body: TaskUpdate, db: db_module = Depends(get_db)): 
    existing = db.get_by_id(task_id) 
    if not existing: 
        raise HTTPException(status_code=404, detail="Task not found") 
    updated = existing.model_copy( 
        update={k: v for k, v in body.model_dump(exclude_unset=True).items()}, 
    ) 
    updated.updated_at = datetime.now(timezone.utc) 
    return db.update(task_id, updated) 

@router.delete("/{task_id}", status_code=204) 
def delete_task(task_id: int, db: db_module = Depends(get_db)): 
    if not db.delete(task_id): 
        raise HTTPException(status_code=404, detail="Task not found") 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key details: the list endpoint supports optional status and priority query parameters, delegating the filtering to the database layer. The create endpoint returns 201 Created, the delete endpoint returns 204 No Content, and missing resources get a 404. For updates, Kiro used PATCH — the correct HTTP verb for partial updates — with Pydantic's model_copy and exclude_unset to only modify the fields that were actually sent. &lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Kiro editor showing routes.py with CRUD endpoints]&lt;/em&gt; &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzkpkpdrw5d5hx423qs6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyzkpkpdrw5d5hx423qs6.png" alt=" " width="800" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Kiro added two production-relevant features: request logging middleware and a global exception handler. &lt;/p&gt;

&lt;p&gt;The middleware logs every request with its method, path, status code, and response time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.middleware("http") 
async def log_requests(request: Request, call_next): 
    start = time.perf_counter() 
    response = await call_next(request) 
    elapsed_ms = (time.perf_counter() - start) * 1000 
    logger.info("%s %s → %d (%.1fms)", request.method, request.url.path, response.status_code, elapsed_ms) 
    return response 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The global exception handler ensures that unhandled errors return a clean JSON response rather than exposing internal details:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@app.exception_handler(Exception) 
async def global_exception_handler(request: Request, exc: Exception): 
    logger.exception("Unhandled error on %s %s", request.method, request.url.path) 
    return JSONResponse(status_code=500, content={"detail": "Internal server error"}) 

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kiro also used FastAPI's modern lifespan context manager to seed the database on startup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@asynccontextmanager 
async def lifespan(app: FastAPI): 
    database.seed() 
    logger.info("Database seeded with %d tasks", len(database.get_all())) 
    yield 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running the Application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt 
uvicorn main:app --reload 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Swagger UI is available at &lt;code&gt;http://localhost:8000/docs&lt;/code&gt; with full interactive documentation — every endpoint, request/response schema, and enum value is documented automatically from the Pydantic models and route definitions. &lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Swagger UI at /docs showing all endpoints and the Task Management API title]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtk9iganwrahhgovhook.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmtk9iganwrahhgovhook.png" alt=" " width="800" height="520"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The API includes seed data, so you can verify all endpoints immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# List all tasks 
curl http://localhost:8000/tasks 

# Filter by status 
curl "http://localhost:8000/tasks?status=todo" 

# Create a new task 
curl -X POST http://localhost:8000/tasks \ 
  -H "Content-Type: application/json" \ 
  -d '{"title":"Ship the feature","priority":"high"}' 

# Handle a missing resource 
curl http://localhost:8000/tasks/999 
# → {"detail": "Task not found"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All endpoints returned correct responses on the first run — no import errors, no configuration issues, no missing dependencies. &lt;/p&gt;

&lt;p&gt;The initial scaffolding gives you a working API, but a real service needs authentication. Rather than wiring that up manually, I asked Kiro to layer it on: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add API key authentication middleware to the task management API. Protect all /tasks endpoints. Include a hardcoded API key for demo purposes and return 401 for missing or invalid keys." &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Kiro chat panel showing the authentication follow-up prompt]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2550wlso2mg86yo05t2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj2550wlso2mg86yo05t2.png" alt=" " width="614" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and wired it into the existing route structure without touching the business logic. Here is the key piece it generated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import Security, HTTPException, status 
from fastapi.security import APIKeyHeader 

API_KEY = "demo-secret-key-2024" 
api_key_header = APIKeyHeader(name="X-API-Key", auto_error=False) 

async def verify_api_key(api_key: str = Security(api_key_header)): 
    if not api_key or api_key != API_KEY: 
        raise HTTPException( 
            status_code=status.HTTP_401_UNAUTHORIZED, 
            detail="Invalid or missing API key", 
        ) 
    return api_key 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The route file then picks up the new dependency alongside the existing database injection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@router.post("", response_model=Task, status_code=201) 
def create_task( 
    body: TaskCreate, 
    db: db_module = Depends(get_db), 
    api_key: str = Depends(verify_api_key), 
): 
    ... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the strength of the dependency injection pattern Kiro set up in the first pass — adding authentication is additive, not invasive. The database layer, models, and error handling are all untouched. &lt;/p&gt;

&lt;p&gt;After restarting the server, the Swagger UI at &lt;a href="http://localhost:8000/docs" rel="noopener noreferrer"&gt;http://localhost:8000/docs&lt;/a&gt; automatically shows an "Authorize" button where you can enter the API key. Requests without the X-API-Key header now return a 401: &lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Swagger UI showing the Authorize button at the top after adding auth and showing 401 response when calling GET /tasks without API key]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyi4v6j9yng86stqmxlax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyi4v6j9yng86stqmxlax.png" alt=" " width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Kiro code showing API key that need to be added to authorize the API calls]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzk75p7nsxnm3yul8ua6n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzk75p7nsxnm3yul8ua6n.png" alt=" " width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;📸 &lt;em&gt;[SCREENSHOT: Swagger UI showing successful response after authorizing with the API key]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe64qb1tiyffiy6xcl588.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe64qb1tiyffiy6xcl588.png" alt=" " width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcij8qzzo4oml6v7mp9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcij8qzzo4oml6v7mp9g.png" alt=" " width="800" height="422"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Without API key — rejected 
curl http://localhost:8000/tasks 
# → {"detail": "Invalid or missing API key"} 

# With API key — works as before 
curl -H "X-API-Key: demo-secret-key-2024" http://localhost:8000/tasks 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The point here is the workflow: scaffold first, then iterate with follow-up prompts. Each prompt builds on the existing structure rather than starting over. Kiro understands the codebase it just generated and extends it in place. &lt;/p&gt;

&lt;p&gt;Moving Beyond the Demo: Production Authentication &lt;/p&gt;

&lt;p&gt;The hardcoded API key above is intentionally simple — it keeps the demo focused on the workflow pattern. In a production service, you would never store secrets in source code. Here is how you might prompt Kiro for a production-ready authentication layer: &lt;/p&gt;

&lt;p&gt;"Replace the hardcoded API key with environment variable configuration using python-dotenv. Load the key from a .env file, add .env to .gitignore, and return a clear error on startup if the key is not set." &lt;/p&gt;

&lt;p&gt;Or for a more complete auth system: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add JWT-based authentication with login and token refresh endpoints. Use python-jose for token handling and passlib for password hashing. Store users in the in-memory database with hashed passwords. Protect all /tasks endpoints with a Bearer token dependency."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key security considerations for production: &lt;/p&gt;

&lt;p&gt;Secrets loaded from environment variables or a secrets manager, never committed to source &lt;/p&gt;

&lt;p&gt;Token-based auth (JWT or OAuth2) instead of static API keys for user-facing APIs &lt;/p&gt;

&lt;p&gt;Password hashing with bcrypt or argon2, never stored in plaintext &lt;/p&gt;

&lt;p&gt;Token expiration and refresh flows to limit the blast radius of a leaked token &lt;/p&gt;

&lt;p&gt;HTTPS enforced at the load balancer or reverse proxy level &lt;/p&gt;

&lt;p&gt;The dependency injection pattern Kiro set up in the initial scaffolding makes any of these upgrades clean — you swap the auth dependency, and the routes stay the same. &lt;/p&gt;

&lt;p&gt;The same follow-up prompt pattern works for other features: &lt;/p&gt;

&lt;p&gt;Background tasks — "Send a notification when a task status changes to done" — Kiro can use FastAPI's &lt;code&gt;BackgroundTasks&lt;/code&gt; to add async processing without blocking the response. &lt;/p&gt;

&lt;p&gt;Persistent storage — "Replace the in-memory store with SQLAlchemy and SQLite" — the dependency injection pattern makes this swap clean; you modify &lt;code&gt;get_db()&lt;/code&gt; and the routes stay the same. &lt;/p&gt;

&lt;p&gt;Pagination — "Add offset/limit pagination to the list endpoint" — a small change to the query parameters and database method. &lt;/p&gt;

&lt;p&gt;Summary &lt;/p&gt;

&lt;p&gt;Starting from a single natural language prompt, Kiro produced a complete FastAPI application with validated models, dependency injection, CRUD endpoints with filtering, middleware, error handling, and auto-generated Swagger docs. A follow-up prompt then layered on API key authentication without disrupting the existing code. &lt;/p&gt;

&lt;p&gt;Your results will follow the same patterns and architecture, though the exact code may vary between runs — that is the nature of AI-assisted generation. The value is in getting the structure, conventions, and wiring right from the start, so you can focus on the logic that makes your API unique. &lt;/p&gt;

&lt;p&gt;@kirodotdev &lt;/p&gt;

</description>
      <category>kiro</category>
      <category>fastapi</category>
      <category>python</category>
      <category>api</category>
    </item>
    <item>
      <title>Create Stunning Videos with JavaScript Using Remotion &amp; Kiro IDE</title>
      <dc:creator>Bharadwaz Kari</dc:creator>
      <pubDate>Tue, 27 Jan 2026 06:03:52 +0000</pubDate>
      <link>https://dev.to/zendizmo/create-stunning-videos-with-javascript-using-remotion-kiro-ide-12l0</link>
      <guid>https://dev.to/zendizmo/create-stunning-videos-with-javascript-using-remotion-kiro-ide-12l0</guid>
      <description>&lt;p&gt;What if you could create professional videos by writing React code — and have AI generate most of it for you?&lt;/p&gt;

&lt;p&gt;That's exactly what happens when you combine &lt;strong&gt;Remotion&lt;/strong&gt; (a React framework for programmatic video) with &lt;strong&gt;Kiro IDE&lt;/strong&gt; (an AI-powered IDE from AWS).&lt;/p&gt;

&lt;p&gt;In this post, I'll show you how to set this up in under 2 minutes and start creating videos from natural language prompts.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Remotion?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://remotion.dev" rel="noopener noreferrer"&gt;Remotion&lt;/a&gt; lets you create videos using React and JavaScript. Instead of dragging clips in a video editor, you write code.&lt;/p&gt;

&lt;p&gt;Why would you want that?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic control&lt;/strong&gt; — Generate hundreds of personalized videos from data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version control&lt;/strong&gt; — Your videos are code, track changes with Git&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusability&lt;/strong&gt; — Build components once, use them everywhere&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simple fade-in animation in Remotion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCurrentFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useVideoConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;interpolate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remotion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;FadeIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCurrentFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVideoConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extrapolateRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;72&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;white&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Hello World
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice something? No CSS animations. Remotion uses &lt;code&gt;useCurrentFrame()&lt;/code&gt; to drive everything — that's how it renders each frame deterministically.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: AI Gets Remotion Wrong
&lt;/h2&gt;

&lt;p&gt;Here's the catch: Remotion has specific patterns that most AI coding assistants get wrong.&lt;/p&gt;

&lt;p&gt;❌ CSS animations — don't render correctly&lt;br&gt;&lt;br&gt;
❌ Tailwind animate classes — break during export&lt;br&gt;&lt;br&gt;
❌ Missing &lt;code&gt;extrapolateRight: 'clamp'&lt;/code&gt; — values go out of bounds&lt;br&gt;&lt;br&gt;
❌ Using &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;lt;Img&amp;gt;&lt;/code&gt; — images don't load before render  &lt;/p&gt;

&lt;p&gt;When you ask ChatGPT or Copilot to write Remotion code, you often get broken output that looks fine in preview but fails during render.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Solution: Kiro + Steering Files
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; is an AI-powered IDE from AWS. What makes it different is &lt;strong&gt;steering files&lt;/strong&gt; — markdown documents that teach the AI your project's patterns.&lt;/p&gt;

&lt;p&gt;Drop Remotion's best practices into &lt;code&gt;.kiro/steering/&lt;/code&gt; and Kiro follows them automatically. No more explaining the same rules over and over.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Kiro Does Differently
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Steering files&lt;/strong&gt; — Persistent context that applies to every interaction&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec-driven development&lt;/strong&gt; — Break features into requirements → design → tasks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vibe mode&lt;/strong&gt; — Autopilot that creates and modifies multiple files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; — Automated actions triggered by events (save, prompt, etc.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For Remotion, steering files are huge. We give Kiro all the rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;useCurrentFrame()&lt;/code&gt;, not CSS animations&lt;/li&gt;
&lt;li&gt;Always clamp interpolations&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;&amp;lt;Img&amp;gt;&lt;/code&gt; from remotion, not &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;spring()&lt;/code&gt; with proper damping configs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it follows them every time.&lt;/p&gt;


&lt;h2&gt;
  
  
  Setup (60 Seconds)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Step 1: Create a Remotion project
&lt;/h3&gt;

&lt;p&gt;Options: Blank Project, TailwindCSS, No Agents, Not VS Code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-video@latest my-video
&lt;span class="nb"&gt;cd &lt;/span&gt;my-video
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Add Remotion skills to Kiro
&lt;/h3&gt;

&lt;p&gt;Run this single command to pull in all the Remotion best practices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/remotion-dev/skills.git temp-skills&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .kiro/steering&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cp &lt;/span&gt;temp-skills/skills/remotion/SKILL.md .kiro/steering/remotion-rules.md&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; temp-skills/skills/remotion/rules .kiro/steering/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; temp-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Open in Kiro
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The &lt;code&gt;.kiro/steering/&lt;/code&gt; folder now contains 30+ rule files covering animations, timing, sequencing, images, audio, transitions, and more.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You Can Build
&lt;/h2&gt;

&lt;p&gt;Once set up, try prompts like these in Kiro's Vibe mode:&lt;/p&gt;

&lt;h3&gt;
  
  
  Simple animation
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a fade-in animation that reveals text over 1 second"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Multi-scene video
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a 10-second product launch video with 3 scenes:&lt;/p&gt;

&lt;p&gt;Scene 1 (0-3s): Dark background, product name 'LaunchPad' fades in with a glow&lt;/p&gt;

&lt;p&gt;Scene 2 (3-6s): Product name slides left, three feature bullets animate in from the right&lt;/p&gt;

&lt;p&gt;Scene 3 (7-10s): Call-to-action 'Try it free' scales up with bounce effect&lt;/p&gt;

&lt;p&gt;Use dark blue gradient background and cyan accent color"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  With specific effects
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add floating particles in the background"&lt;br&gt;
"Make the text spring in from the right with a bounce"&lt;br&gt;
"Add a neural network animation with pulsing nodes"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kiro generates correct Remotion code — proper &lt;code&gt;spring()&lt;/code&gt; animations, &lt;code&gt;&amp;lt;Sequence&amp;gt;&lt;/code&gt; timing, premounting, the works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Example Output
&lt;/h2&gt;

&lt;p&gt;Here's what Kiro generates for a multi-scene intro (simplified):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;AbsoluteFill&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useCurrentFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useVideoConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remotion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ProductLaunch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCurrentFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVideoConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AbsoluteFill&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;linear-gradient(135deg, #0a1628, #1a2744)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Scene 1: Logo reveal */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;durationInFrames&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;premountFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LogoReveal&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Scene 2: Features */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;durationInFrames&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;premountFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FeatureBullets&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Scene 3: CTA */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;durationInFrames&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;premountFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CallToAction&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Sequence&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AbsoluteFill&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LogoReveal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCurrentFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVideoConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;damping&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;opacity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;interpolate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fps&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;extrapolateRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clamp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AbsoluteFill&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;alignItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`scale(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;fontWeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bold&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#00d4ff&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;textShadow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 0 40px rgba(0, 212, 255, 0.5)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        LaunchPad
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;AbsoluteFill&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;useCurrentFrame()&lt;/code&gt; drives all animations&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;spring()&lt;/code&gt; with &lt;code&gt;damping: 200&lt;/code&gt; for smooth motion&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;interpolate()&lt;/code&gt; with &lt;code&gt;extrapolateRight: 'clamp'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;✅ &lt;code&gt;&amp;lt;Sequence&amp;gt;&lt;/code&gt; with &lt;code&gt;premountFor&lt;/code&gt; for preloading&lt;/li&gt;
&lt;li&gt;✅ Proper timing in frames (fps-based)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the Remotion patterns, automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rendering
&lt;/h2&gt;

&lt;p&gt;Preview your video:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to see the Remotion Studio with hot reload.&lt;/p&gt;

&lt;p&gt;Export the final video:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx remotion render ProductLaunch out/video.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output Result 

  &lt;iframe src="https://www.youtube.com/embed/q0lwaYYjECU"&gt;
  &lt;/iframe&gt;



&lt;/h2&gt;

&lt;h2&gt;
  
  
  Patterns Kiro Handles
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What Kiro Uses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Animations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;useCurrentFrame()&lt;/code&gt; + &lt;code&gt;interpolate()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;spring()&lt;/code&gt; with damping configs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sequencing&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;Sequence&amp;gt;&lt;/code&gt; with &lt;code&gt;premountFor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transitions&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;TransitionSeries&amp;gt;&lt;/code&gt; from &lt;code&gt;@remotion/transitions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;Img&amp;gt;&lt;/code&gt; from &lt;code&gt;remotion&lt;/code&gt; (not &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Videos&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;Video&amp;gt;&lt;/code&gt; from &lt;code&gt;@remotion/media&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Audio&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&amp;lt;Audio&amp;gt;&lt;/code&gt; from &lt;code&gt;@remotion/media&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@remotion/google-fonts&lt;/code&gt; with &lt;code&gt;loadFont()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Global Setup (Optional)
&lt;/h2&gt;

&lt;p&gt;Want Remotion skills in ALL your Kiro projects?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.kiro/steering
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/remotion-dev/skills.git /tmp/remotion-skills
&lt;span class="nb"&gt;cp&lt;/span&gt; /tmp/remotion-skills/skills/remotion/SKILL.md ~/.kiro/steering/remotion-rules.md
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; /tmp/remotion-skills/skills/remotion/rules ~/.kiro/steering/
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /tmp/remotion-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every workspace gets Remotion knowledge automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips for Best Results
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Vibe mode&lt;/strong&gt; — Kiro's autopilot creates multiple files at once and iterates quickly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Describe scenes in plain English&lt;/strong&gt; — Be specific about timing, colors, and effects&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Iterate visually&lt;/strong&gt; — The Remotion Studio hot-reloads, so ask Kiro to tweak and see changes instantly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add audio last&lt;/strong&gt; — Get the visuals right first, then layer in music:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Audio&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@remotion/media&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;staticFile&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remotion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Audio&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;staticFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;music.mp3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;volume&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use YouTube Audio Library&lt;/strong&gt; — Free royalty-free music at &lt;a href="https://studio.youtube.com/channel/UC/music" rel="noopener noreferrer"&gt;studio.youtube.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://remotion.dev/docs" rel="noopener noreferrer"&gt;Remotion Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/remotion-dev/skills" rel="noopener noreferrer"&gt;Remotion Skills Repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro IDE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/steering" rel="noopener noreferrer"&gt;Kiro Steering Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create project&lt;/span&gt;
npx create-video@latest my-video
&lt;span class="nb"&gt;cd &lt;/span&gt;my-video &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# Add Remotion skills to Kiro&lt;/span&gt;
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/remotion-dev/skills.git temp-skills&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; .kiro/steering&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cp &lt;/span&gt;temp-skills/skills/remotion/SKILL.md .kiro/steering/remotion-rules.md&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; temp-skills/skills/remotion/rules .kiro/steering/&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; temp-skills

&lt;span class="c"&gt;# Open in Kiro and start building&lt;/span&gt;
kiro &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then describe what you want. Kiro generates correct Remotion code. Ship videos.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions or want to share what you've built? Drop a comment below!&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; #remotion #kiro #aws #javascript #react #video #ai #webdev #tutorial&lt;/p&gt;

</description>
      <category>ai</category>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
