New Loading Spinner Component #1694
Replies: 13 comments 26 replies
-
Agreed, this seems to be something missing! |
Beta Was this translation helpful? Give feedback.
-
export const LoadingSpinner = ({className}) => {
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={cn("animate-spin", className)}
>
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
} |
Beta Was this translation helpful? Give feedback.
-
@benjick is shared a good example. I improved a bit and add type-safety and flexibility. export interface ISVGProps extends React.SVGProps<SVGSVGElement> {
size?: number;
className?: string;
}
export const LoadingSpinner = ({
size = 24,
className,
...props
}: ISVGProps) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
{...props}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
className={cn("animate-spin", className)}
>
<path d="M21 12a9 9 0 1 1-6.219-8.56" />
</svg>
);
}; |
Beta Was this translation helpful? Give feedback.
-
how can i use this loadingspinner |
Beta Was this translation helpful? Give feedback.
-
take a look at lucide-react package. it is as simple as: import { LoaderIcon } from "lucide-react"
<LoaderIcon className="animate-spin" /> |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
created it here |
Beta Was this translation helpful? Give feedback.
-
To anybody who came here for the common loading spinner, you see on a lot of websites, here it is:
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
const spinnerVariants =
"w-16 h-16 border-4 border-t-4 border-gray-200 border-t-gray-600 rounded-full animate-spin";
const LoadingSpinner = React.forwardRef((props, ref) => {
const { className, ...rest } = props;
return <div ref={ref} className={cn(spinnerVariants, className)} {...rest} />;
});
LoadingSpinner.displayName = "LoadingSpinner";
export { LoadingSpinner }; The code is consistent with the other Shadcn UI components.
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
const spinnerVariants =
"w-16 h-16 border-4 border-t-4 border-gray-200 border-t-gray-600 rounded-full animate-spin";
interface LoadingSpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string;
}
const LoadingSpinner = React.forwardRef<HTMLDivElement, LoadingSpinnerProps>((props, ref) => {
const { className, ...rest } = props;
return <div ref={ref} className={cn(spinnerVariants, className)} {...rest} />;
});
LoadingSpinner.displayName = "LoadingSpinner";
export { LoadingSpinner }; You can use it anywhere in your app like this: <LoadingSpinner /> |
Beta Was this translation helpful? Give feedback.
-
And here goes the variant that works and combines lucide icons with Shadcn code conventions: import * as React from "react";
import { cn } from "@/lib/utils";
import { LoaderIcon } from "lucide-react";
const spinnerVariants = "w-16 h-16 rounded-full animate-spin";
interface LoadingSpinnerProps extends React.HTMLAttributes<SVGSVGElement> {
className?: string;
}
const LoadingSpinner = React.forwardRef<SVGSVGElement, LoadingSpinnerProps>(
(props, ref) => {
const { className, ...rest } = props;
return (
<LoaderIcon
ref={ref}
className={cn(spinnerVariants, className)}
{...rest}
/>
);
},
);
LoadingSpinner.displayName = "LoadingSpinner";
export { LoadingSpinner }; |
Beta Was this translation helpful? Give feedback.
-
Based on @mazewinther code published here.
'use client'
import * as React from 'react'
import { cn } from '@/lib/utils'
// Define the spinner styles as a constant for better readability and maintainability
const spinnerStyles = 'border-4 border-t-4 rounded-full animate-spin'
// Define the default colors for the spinner
const defaultSpinnerColors = {
border: 'gray-200',
borderTop: 'gray-900',
size: 'w-16 h-16',
}
interface LoadingSpinnerProps extends React.HTMLAttributes<HTMLDivElement> {
className?: string
borderColor?: string
borderTopColor?: string
size?: number | string
}
/**
* A simple loading spinner component.
*
* @param props - The props for the component.
* @returns The loading spinner element.
*/
const LoadingSpinner = React.forwardRef<HTMLDivElement, LoadingSpinnerProps>(
(props, ref) => {
const { className, borderColor, borderTopColor, size, ...rest } = props
const borderStyle = cn(
`${spinnerStyles} border-${borderColor || defaultSpinnerColors.border} border-t-${
borderTopColor || defaultSpinnerColors.borderTop
}`,
)
const sizeStyle = size ? `h-${size} w-${size}` : defaultSpinnerColors.size
return (
<div className={cn(sizeStyle, className)} ref={ref} {...rest}>
<div className={cn(borderStyle, sizeStyle)} />
</div>
)
},
)
LoadingSpinner.displayName = 'LoadingSpinner'
export { LoadingSpinner } You can use it anywhere in your app like this: <LoadingSpinner borderColor="gray-200" borderTopColor="gray-600" size={4}/> |
Beta Was this translation helpful? Give feedback.
-
I like using the combo tailwind + vector icons with Usage: <Spinner className="w-4 h-4 mr-2" type="circle-bars" />
<Spinner className="w-4 h-4 mr-2" type="shaft-long-rounded" />
<Spinner className="w-4 h-4 mr-2" type="shaft-short-rounded" /> |
Beta Was this translation helpful? Give feedback.
-
I've created one similar to the example shared by @henriemategui in the initial discussion. components/spinner.tsx/**
* A loading spinner component that supports multiple variants and sizes.
* @example
* ```tsx
* <Spinner variant="primary" size="lg" />
* <Spinner size={32} />
* ```
*/
import * as React from 'react';
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@/lib/utils';
const spinnerVariants = cva(
'relative inline-block aspect-square transform-gpu',
{
variants: {
variant: {
default: '[&>div]:bg-foreground',
primary: '[&>div]:bg-primary',
secondary: '[&>div]:bg-secondary',
destructive: '[&>div]:bg-destructive',
muted: '[&>div]:bg-muted-foreground',
},
size: {
sm: 'size-4',
default: 'size-5',
lg: 'size-8',
},
},
defaultVariants: { variant: 'default', size: 'default' },
},
);
export interface SpinnerProps
extends React.HTMLAttributes<HTMLDivElement>,
Omit<VariantProps<typeof spinnerVariants>, 'size'> {
className?: string;
size?: VariantProps<typeof spinnerVariants>['size'] | number;
}
const Spinner = ({ className, variant, size = 'default' }: SpinnerProps) => (
<div
role="status"
aria-label="Loading"
className={cn(
typeof size === 'string'
? spinnerVariants({ variant, size })
: spinnerVariants({ variant }),
className,
)}
style={typeof size === 'number' ? { width: size, height: size } : undefined}
>
{Array.from({ length: 12 }).map((_, i) => (
<div
key={i}
className="animate-spinner absolute left-[46.5%] top-[4.4%] h-[24%] w-[7%]
origin-[center_190%] rounded-full opacity-[0.1] will-change-transform"
style={{
transform: `rotate(${i * 30}deg)`,
animationDelay: `${(i * 0.083).toFixed(3)}s`,
}}
aria-hidden="true"
/>
))}
<span className="sr-only">Loading...</span>
</div>
);
export { Spinner, spinnerVariants }; tailwind.config.tsmodule.exports = {
extend: {
animation: {
spinner: 'spinner 1s linear infinite',
},
keyframes: {
spinner: {
'0%': { opacity: '1' },
'10%': { opacity: '0.7' },
'20%': { opacity: '0.3' },
'35%': { opacity: '0.2' },
'50%': { opacity: '0.1' },
'75%': { opacity: '0.05' },
'100%': { opacity: '0' },
},
},
},
} Usage<Spinner variant="primary" size="lg" /> or <Spinner size={32} /> |
Beta Was this translation helpful? Give feedback.
-
A component that indicates interface loading would be interesting, I know Skeleton already exists, but that's not what I'm talking about. It's something like a spinner.
I know this isn't a big deal and it doesn't take much work for me to do myself, but I like shadcn/ui's design patterns and I would love it to have that.
An example:
Beta Was this translation helpful? Give feedback.
All reactions