Progress
An element that shows either determinate or indeterminate progress.
'use client'
import { useEffect, useState } from 'react'
import { Progress, type ProgressProps } from '~/components/ui/progress'
export const Demo = (props: ProgressProps) => {
  const [value, setValue] = useState(0)
  useEffect(() => {
    const interval = setInterval(() => {
      setValue((value) => (value === 100 ? 0 : value + 1))
    }, Math.random() * 500)
    return () => clearInterval(interval)
  })
  return <Progress {...props} value={value} min={0} max={100} />
}
import { createEffect, createSignal } from 'solid-js'
import { Progress, type ProgressProps } from '~/components/ui/progress'
export const Demo = (props: ProgressProps) => {
  const [value, setValue] = createSignal(0)
  createEffect(() => {
    const interval = setInterval(() => {
      setValue((prevValue) => (prevValue === 100 ? 0 : prevValue + 1))
    }, Math.random() * 500)
    return () => clearInterval(interval)
  })
  return <Progress {...props} value={value()} min={0} max={100} />
}
Usage
import { Progress } from '~/components/ui/progress'Installation
npx @park-ui/cli components add progress1
Add Styled Primitive
Copy the code snippet below into ~/components/ui/styled/progress.tsx
'use client'
import type { Assign } from '@ark-ui/react'
import { Progress } from '@ark-ui/react/progress'
import { type ProgressVariantProps, progress } from 'styled-system/recipes'
import type { ComponentProps, HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(progress)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, Progress.RootProviderBaseProps>, ProgressVariantProps>
>(Progress.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  HTMLDivElement,
  Assign<Assign<HTMLStyledProps<'div'>, Progress.RootBaseProps>, ProgressVariantProps>
>(Progress.Root, 'root')
export const Circle = withContext<
  SVGSVGElement,
  Assign<HTMLStyledProps<'svg'>, Progress.CircleBaseProps>
>(Progress.Circle, 'circle')
export const CircleRange = withContext<
  SVGCircleElement,
  Assign<HTMLStyledProps<'circle'>, Progress.CircleRangeBaseProps>
>(Progress.CircleRange, 'circleRange')
export const CircleTrack = withContext<
  SVGCircleElement,
  Assign<HTMLStyledProps<'circle'>, Progress.CircleTrackBaseProps>
>(Progress.CircleTrack, 'circleTrack')
export const Label = withContext<
  HTMLLabelElement,
  Assign<HTMLStyledProps<'label'>, Progress.LabelBaseProps>
>(Progress.Label, 'label')
export const Range = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, Progress.RangeBaseProps>
>(Progress.Range, 'range')
export const Track = withContext<
  HTMLDivElement,
  Assign<HTMLStyledProps<'div'>, Progress.TrackBaseProps>
>(Progress.Track, 'track')
export const ValueText = withContext<
  HTMLSpanElement,
  Assign<HTMLStyledProps<'span'>, Progress.ValueTextBaseProps>
>(Progress.ValueText, 'valueText')
export const View = withContext<
  HTMLSpanElement,
  Assign<HTMLStyledProps<'span'>, Progress.ViewBaseProps>
>(Progress.View, 'view')
export { ProgressContext as Context } from '@ark-ui/react/progress'
import { type Assign, Progress } from '@ark-ui/solid'
import type { ComponentProps } from 'solid-js'
import { type ProgressVariantProps, progress } from 'styled-system/recipes'
import type { HTMLStyledProps } from 'styled-system/types'
import { createStyleContext } from './utils/create-style-context'
const { withProvider, withContext } = createStyleContext(progress)
export type RootProviderProps = ComponentProps<typeof RootProvider>
export const RootProvider = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, Progress.RootProviderBaseProps>, ProgressVariantProps>
>(Progress.RootProvider, 'root')
export type RootProps = ComponentProps<typeof Root>
export const Root = withProvider<
  Assign<Assign<HTMLStyledProps<'div'>, Progress.RootBaseProps>, ProgressVariantProps>
>(Progress.Root, 'root')
export const Circle = withContext<Assign<HTMLStyledProps<'svg'>, Progress.CircleBaseProps>>(
  Progress.Circle,
  'circle',
)
export const CircleRange = withContext<
  Assign<HTMLStyledProps<'circle'>, Progress.CircleRangeBaseProps>
>(Progress.CircleRange, 'circleRange')
export const CircleTrack = withContext<
  Assign<HTMLStyledProps<'circle'>, Progress.CircleTrackBaseProps>
>(Progress.CircleTrack, 'circleTrack')
export const Label = withContext<Assign<HTMLStyledProps<'label'>, Progress.LabelBaseProps>>(
  Progress.Label,
  'label',
)
export const Range = withContext<Assign<HTMLStyledProps<'div'>, Progress.RangeBaseProps>>(
  Progress.Range,
  'range',
)
export const Track = withContext<Assign<HTMLStyledProps<'div'>, Progress.TrackBaseProps>>(
  Progress.Track,
  'track',
)
export const ValueText = withContext<Assign<HTMLStyledProps<'span'>, Progress.ValueTextBaseProps>>(
  Progress.ValueText,
  'valueText',
)
export const View = withContext<Assign<HTMLStyledProps<'span'>, Progress.ViewBaseProps>>(
  Progress.View,
  'view',
)
export { ProgressContext as Context } from '@ark-ui/solid'
No snippet found2
Add Composition
Copy the composition snippet below into ~/components/ui/progress.tsx
import { forwardRef } from 'react'
import * as StyledProgress from './styled/progress'
export interface ProgressProps extends StyledProgress.RootProps {
  /**
   * The type of progress to render.
   * @default linear
   */
  type?: 'linear' | 'circular'
}
export const Progress = forwardRef<HTMLDivElement, ProgressProps>((props, ref) => {
  const { children, type = 'linear', ...rootProps } = props
  return (
    <StyledProgress.Root ref={ref} {...rootProps}>
      {children && <StyledProgress.Label>{children}</StyledProgress.Label>}
      {type === 'linear' && (
        <StyledProgress.Track>
          <StyledProgress.Range />
        </StyledProgress.Track>
      )}
      {type === 'circular' && (
        <StyledProgress.Circle>
          <StyledProgress.CircleTrack />
          <StyledProgress.CircleRange />
          <StyledProgress.ValueText />
        </StyledProgress.Circle>
      )}
      <StyledProgress.ValueText />
    </StyledProgress.Root>
  )
})
Progress.displayName = 'Progress'
import { Show, children, splitProps } from 'solid-js'
import * as StyledProgress from './styled/progress'
export interface ProgressProps extends StyledProgress.RootProps {
  /**
   * The type of progress to render.
   * @default linear
   */
  type?: 'linear' | 'circular'
}
export const Progress = (props: ProgressProps) => {
  const [localProps, rootProps] = splitProps(props, ['children', 'type'])
  const getChildren = children(() => localProps.children)
  return (
    <StyledProgress.Root {...rootProps}>
      <Show when={getChildren()}>
        <StyledProgress.Label>{getChildren()}</StyledProgress.Label>
      </Show>
      <Show
        when={localProps.type === 'circular'}
        fallback={
          <StyledProgress.Track>
            <StyledProgress.Range />
          </StyledProgress.Track>
        }
      >
        <StyledProgress.Circle>
          <StyledProgress.CircleTrack />
          <StyledProgress.CircleRange />
          <StyledProgress.ValueText />
        </StyledProgress.Circle>
      </Show>
      <StyledProgress.ValueText />
    </StyledProgress.Root>
  )
}
3
Integrate Recipe
If you're not using @park-ui/preset, add the following recipe to yourpanda.config.ts:
import { progressAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'
export const progress = defineSlotRecipe({
  className: 'progress',
  slots: progressAnatomy.keys(),
  base: {
    root: {
      alignItems: 'center',
      display: 'flex',
      flexDirection: 'column',
      gap: '1.5',
      width: 'full',
    },
    label: {
      color: 'fg.default',
      fontWeight: 'medium',
      textStyle: 'sm',
    },
    track: {
      backgroundColor: 'bg.emphasized',
      borderRadius: 'l2',
      overflow: 'hidden',
      width: '100%',
    },
    range: {
      backgroundColor: 'colorPalette.default',
      height: '100%',
      transition: 'width 0.2s ease-in-out',
      '--translate-x': '-100%',
    },
    circleTrack: {
      stroke: 'bg.emphasized',
    },
    circleRange: {
      stroke: 'colorPalette.default',
      transitionProperty: 'stroke-dasharray, stroke',
      transitionDuration: '0.6s',
    },
    valueText: {
      textStyle: 'sm',
    },
  },
  defaultVariants: {
    size: 'md',
  },
  variants: {
    size: {
      sm: {
        circle: {
          '--size': '36px',
          '--thickness': '4px',
        },
        track: {
          height: '1.5',
        },
      },
      md: {
        track: {
          height: '2',
        },
        circle: {
          '--size': '40px',
          '--thickness': '4px',
        },
      },
      lg: {
        track: {
          height: '2.5',
        },
        circle: {
          '--size': '44px',
          '--thickness': '4px',
        },
      },
    },
  },
})