Loading Swap
Swaps child content with a loading spinner without changing the element size.
"use client";
import { Button } from "@/components/ui/button";import { LoadingSwap } from "@/components/ui/loading-swap";import { useTransition } from "react";
export function LoadingButton() { const [isLoading, startTransition] = useTransition();
return ( <Button onClick={() => { startTransition(async () => { // Simulate loading state await new Promise((res) => setTimeout(res, 1000)); }); }} > <LoadingSwap isLoading={isLoading}>Click Me</LoadingSwap> </Button> );}Installation
Section titled “Installation”This installation provides support for all official Shadcn icon libraries. The component is otherwise identical to the non-experimental installation.
Icon support is experimental and may not be fully stable since it uses internal Shadcn APIs.
Copy and paste the following code into your project.
components/ui/loading-swap.tsx
import { cn } from "@/lib/utils";import { Loader2Icon } from "lucide-react";import type { ReactNode } from "react";
export function LoadingSwap({ isLoading, children, className,}: { isLoading: boolean; children: ReactNode; className?: string;}) { return ( <div className="grid grid-cols-1 items-center justify-items-center"> <div className={cn( "col-start-1 col-end-2 row-start-1 row-end-2 w-full", isLoading ? "invisible" : "visible", className, )} > {children} </div> <div className={cn( "col-start-1 col-end-2 row-start-1 row-end-2", isLoading ? "visible" : "invisible", className, )} > <Loader2Icon className="animate-spin" /> </div> </div> );}Update the import paths to match your project setup.
import { LoadingSwap } from "@/components/ui/loading-swap"<LoadingSwap isLoading>Search</LoadingSwap>Examples
Section titled “Examples”Large components
Section titled “Large components”"use client";
import { Button } from "@/components/ui/button";import { Card, CardDescription, CardHeader, CardTitle,} from "@/components/ui/card";import { LoadingSwap } from "@/components/ui/loading-swap";import { useState } from "react";
export function LoadingButton() { const [isLoading, setIsLoading] = useState(false);
return ( <div className="flex flex-col gap-2"> <Button onClick={() => setIsLoading((l) => !l)} className="w-fit"> Toggle Loading </Button> <Card className="w-96"> <LoadingSwap isLoading={isLoading}> <CardHeader> <CardTitle>Larger Component</CardTitle> <CardDescription> Can be used to wrap any components </CardDescription> </CardHeader> </LoadingSwap> </Card> </div> );}