# nextjs-component > const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000/api', }); - Author: irajfatima001 - Repository: irajfatima001/phase-03 - Version: 20260208223900 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-08 - Source: https://github.com/irajfatima001/phase-03 - Web: https://mule.run/skillshub/@@irajfatima001/phase-03~nextjs-component:20260208223900 --- # Skill: nextjs-component **Name**: nextjs-component **Description**: Generates reusable Next.js TypeScript components with Tailwind, shadcn/ui, forms, and axios Bearer token API calls ## Purpose This skill generates reusable Next.js components using TypeScript, Tailwind CSS, shadcn/ui components, and axios for API calls with Bearer token authentication. The components follow modern best practices for state management, error handling, and responsive design. ## Allowed Tools - code_write - file_edit ## Implementation Instructions ### 1. Component Structure Create components in the `src/components` directory with the following structure: ``` components/ ├── ui/ │ ├── button.tsx │ ├── card.tsx │ ├── input.tsx │ └── ... ├── TaskCard.tsx ├── TaskForm.tsx ├── AuthButton.tsx └── ... ``` ### 2. Axios Instance with Interceptors ```typescript // src/lib/axios-instance.ts import axios from 'axios'; const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:8000/api', }); // Add Bearer token to requests axiosInstance.interceptors.request.use( (config) => { const token = localStorage.getItem('better-auth-token'); // Or however you store the token if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }, (error) => { return Promise.reject(error); } ); // Handle token expiration axiosInstance.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401) { // Handle token expiration (e.g., redirect to login) localStorage.removeItem('better-auth-token'); window.location.href = '/login'; } return Promise.reject(error); } ); export default axiosInstance; ``` ### 3. Example Component: TaskCard ```tsx // src/components/TaskCard.tsx import { Card, CardContent, CardFooter } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Task } from '@/types/task'; import { Trash2, Edit } from 'lucide-react'; import { useState } from 'react'; interface TaskCardProps { task: Task; onEdit: (task: Task) => void; onDelete: (taskId: number) => void; onToggleComplete: (taskId: number) => void; } export function TaskCard({ task, onEdit, onDelete, onToggleComplete }: TaskCardProps) { const [isLoading, setIsLoading] = useState(false); const handleDelete = async () => { setIsLoading(true); try { await onDelete(task.id); } finally { setIsLoading(false); } }; const handleToggleComplete = async () => { setIsLoading(true); try { await onToggleComplete(task.id); } finally { setIsLoading(false); } }; return ( {task.title} {task.description && ( {task.description} )} {task.priority} {task.status} onEdit(task)} disabled={isLoading} > Edit Delete ); } ``` ### 4. Example Component: TaskForm ```tsx // src/components/TaskForm.tsx import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Task } from '@/types/task'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const taskSchema = z.object({ title: z.string().min(1, 'Title is required'), description: z.string().optional(), priority: z.enum(['low', 'medium', 'high']), }); type TaskFormData = z.infer; interface TaskFormProps { task?: Task; onSubmit: (data: TaskFormData) => void; onCancel: () => void; isLoading?: boolean; } export function TaskForm({ task, onSubmit, onCancel, isLoading = false }: TaskFormProps) { const { register, handleSubmit, formState: { errors }, setValue, reset } = useForm({ resolver: zodResolver(taskSchema), defaultValues: { title: task?.title || '', description: task?.description || '', priority: task?.priority || 'medium', } }); const handleFormSubmit = (data: TaskFormData) => { onSubmit(data); reset(); // Reset form after submission }; return ( {task ? 'Edit Task' : 'Create Task'} Title {errors.title && ( {errors.title.message} )} Description Priority setValue('priority', value as 'low' | 'medium' | 'high')} > Low Medium High Cancel {isLoading ? 'Saving...' : task ? 'Update Task' : 'Create Task'} ); } ``` ### 5. Example Component: AuthButton ```tsx // src/components/AuthButton.tsx import { Button } from '@/components/ui/button'; import { signIn, signOut, useUser } from '@clerk/nextjs'; import { LogIn, LogOut } from 'lucide-react'; interface AuthButtonProps { variant?: 'default' | 'outline' | 'secondary' | 'ghost' | 'link'; size?: 'default' | 'sm' | 'lg' | 'icon'; } export function AuthButton({ variant = 'default', size = 'default' }: AuthButtonProps) { const { isSignedIn } = useUser(); return ( { if (isSignedIn) { signOut(); } else { signIn(); } }} > {isSignedIn ? ( <> Sign Out > ) : ( <> Sign In > )} ); } ``` ### 6. Theme Provider for Dark/Light Mode ```tsx // src/providers/ThemeProvider.tsx 'use client'; import * as React from 'react'; import { ThemeProvider as NextThemesProvider } from 'next-themes'; import { type ThemeProviderProps } from 'next-themes/dist/types'; export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children}; } ``` ### 7. Responsive Design Guidelines - Use Tailwind's responsive prefixes (sm:, md:, lg:, xl:, 2xl:) - Implement mobile-first design approach - Ensure touch targets are at least 44px for mobile devices - Use appropriate padding and spacing for different screen sizes ### 8. Loading and Error States ```tsx // Example of handling loading and error states import { useState, useEffect } from 'react'; import { toast } from 'sonner'; // or your preferred toast library export function useApiCall(apiFunction: () => Promise, deps: React.DependencyList = []) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { setLoading(true); setError(null); const result = await apiFunction(); setData(result); } catch (err) { setError(err instanceof Error ? err.message : 'An error occurred'); toast.error('Failed to load data'); } finally { setLoading(false); } }; fetchData(); }, deps); return { data, loading, error }; } ``` ## Key Features - Uses TypeScript for type safety - Implements Next.js App Router structure - Integrates Tailwind CSS for styling - Uses shadcn/ui components for consistent UI - Implements axios with interceptors for API calls - Attaches Bearer token from Better Auth to requests - Handles loading and error states - Responsive design with mobile-first approach - Dark/light mode support - Reusable component architecture ## Best Practices - Use TypeScript interfaces for props and data structures - Implement proper error boundaries - Use React hooks for state management - Follow accessibility guidelines (ARIA attributes, keyboard navigation) - Optimize images and assets - Implement proper loading states - Use Sonner or similar for toast notifications - Follow Next.js best practices for performance
{task.description}
{errors.title.message}