AgentStack
SKILL verified MIT Self-run

Ai Native Ui

skill-leo-atienza-atlas-claude-ai-native-ui · by Leo-Atienza

A Claude skill from Leo-Atienza/atlas-claude.

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-leo-atienza-atlas-claude-ai-native-ui

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Ai Native Ui? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

AI-Native UI

When to Use This Skill

Load when building any UI with LLM integration: chat interfaces, AI-generated content, smart search, content generation tools, or any feature that streams AI responses to users.

This is Tier 4 (Generative) in the Vanguard architecture. Load web-l100 (SK-083) for the full tier system.


The Generative Tier

AI streams are a fundamentally different rendering model. Unlike T0-T3 where the final content is known at render time, T4 content builds up token-by-token with structural uncertainty. The UI must:

  1. Show useful content immediately (PPR shell + cached content)
  2. Progressively reveal AI-generated content as it streams
  3. Handle tool invocations mid-stream (AI calls tools → UI renders components)
  4. Gracefully handle interruption (user cancels, network drops)

Core Pattern: streamText + Route Handler

Server: Route Handler

// app/api/chat/route.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';

export async function POST(req: Request) {
  const { messages } = await req.json();

  const result = streamText({
    model: anthropic('claude-sonnet-4-5-20250514'),
    system: 'You are a helpful assistant.',
    messages,
    tools: {
      getWeather: {
        description: 'Get weather for a location',
        parameters: z.object({
          location: z.string().describe('City name'),
        }),
        execute: async ({ location }) => {
          const weather = await fetchWeather(location);
          return weather;
        },
      },
    },
  });

  return result.toDataStreamResponse();
}

Client: useChat Hook

// components/chat.tsx
'use client';
import { useChat } from '@ai-sdk/react';

export function Chat() {
  const { messages, input, handleInputChange, handleSubmit, isLoading, stop } = useChat({
    api: '/api/chat',
  });

  return (
    
      
        {messages.map(message => (
          
            {message.content}

            {/* Render tool invocations as components */}
            {message.toolInvocations?.map(invocation => (
              
            ))}
          
        ))}
      

      
        
        {isLoading ? (
          Stop
        ) : (
          Send
        )}
      
    
  );
}

Tool Invocations → React Components

The killer pattern: AI calls tools, each tool invocation maps to a React component.

// components/tool-result.tsx
function ToolResult({ invocation }: { invocation: ToolInvocation }) {
  // Tool is still being called — show loading state
  if (invocation.state === 'call') {
    return ;
  }

  // Tool returned a result — render the appropriate component
  if (invocation.state === 'result') {
    switch (invocation.toolName) {
      case 'getWeather':
        return ;
      case 'searchProducts':
        return ;
      case 'getChart':
        return ;
      default:
        return {JSON.stringify(invocation.result, null, 2)};
    }
  }

  return null;
}

function ToolLoading({ name }: { name: string }) {
  return (
    
      
    
  );
}

Streaming Structured Objects

For streaming JSON objects (not just text) as they build up:

Server: streamObject

// app/api/analyze/route.ts
import { streamObject } from 'ai';
import { z } from 'zod';

const AnalysisSchema = z.object({
  summary: z.string(),
  sentiment: z.enum(['positive', 'negative', 'neutral']),
  keyTopics: z.array(z.string()),
  confidence: z.number(),
});

export async function POST(req: Request) {
  const { text } = await req.json();

  const result = streamObject({
    model: anthropic('claude-sonnet-4-5-20250514'),
    schema: AnalysisSchema,
    prompt: `Analyze this text: ${text}`,
  });

  return result.toTextStreamResponse();
}

Client: useObject

'use client';
import { useObject } from '@ai-sdk/react';

function AnalysisPanel({ text }: { text: string }) {
  const { object, isLoading } = useObject({
    api: '/api/analyze',
    schema: AnalysisSchema,
    body: { text },
  });

  return (
    
      {/* Fields appear progressively as AI generates them */}
      {object?.summary && {object.summary}}
      {object?.sentiment && }
      {object?.keyTopics && (
        
          {object.keyTopics.map(topic => {topic})}
        
      )}
      {object?.confidence != null && }
    
  );
}

Generative UI within PPR

The T4 tier integrated with T0-T2. Static shell loads instantly, AI content fills in.

// app/dashboard/page.tsx
export default function AIAssistantPage() {
  return (
    <>
      {/* T0: Static shell — instant from CDN */}
      AI Assistant

      {/* T1: Cached suggestions */}
      

      {/* T4: AI chat — streams in */}
      }>
        
      

      {/* T1: Cached documentation sidebar */}
      
    
  );
}

The user sees the full UI shell instantly, popular questions load from cache, and the AI chat is ready for interaction — all within 100ms. AI responses then stream in 1-3 seconds.


Multi-Stream Orchestration

Multiple independent AI streams in one UI:

function AIDashboard() {
  const summary = useChat({ api: '/api/ai/summary', id: 'summary' });
  const insights = useChat({ api: '/api/ai/insights', id: 'insights' });
  const predictions = useChat({ api: '/api/ai/predictions', id: 'predictions' });

  // Trigger all three on page load
  useEffect(() => {
    summary.append({ role: 'user', content: 'Summarize today' });
    insights.append({ role: 'user', content: 'Key insights' });
    predictions.append({ role: 'user', content: 'Predictions' });
  }, []);

  return (
    
      
      
      
    
  );
}

Each panel updates independently as its stream progresses.


Error Handling & Cancellation

const { messages, error, reload, stop, isLoading } = useChat({
  api: '/api/chat',
  onError: (error) => {
    console.error('Chat error:', error);
    // Show toast notification
  },
});

// Retry last message
{error && (
  Retry
)}

// Cancel in-progress generation
{isLoading && (
  Stop generating
)}

Key Rules

  1. Use AI SDK UI (stable), not AI SDK RSC (experimental) for production
  2. Server returns data, client renders components — clean separation of concerns
  3. Tool invocations are the bridge between AI decisions and React components
  4. Always provide loading states for tool invocations (skeleton matching final size)
  5. Wrap AI components in Suspense for PPR integration
  6. Use stop() — users expect to cancel long generations
  7. Stream objects when structure mattersstreamObject + useObject for dashboards, forms
  8. Cache common AI queries — wrap with use cache for FAQ-style responses

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.