Skip to main content

Quickstart

This guide will walk you through building your first component with Grauity. You’ll create a simple user profile card using multiple Grauity components.
Prerequisites: Make sure you’ve completed the installation steps before proceeding.

What you’ll build

You’ll create a user profile card with:
  • An avatar placeholder
  • User information with typography
  • Action buttons
  • A text field for editing
  • Theme switching
1

Install Grauity

If you haven’t already, install Grauity and its peer dependencies:
npm install @newtonschool/grauity react react-dom styled-components
Import the icon styles in your root CSS/SCSS file:
global-styles.scss
@import '~@newtonschool/grauity/dist/css/index.scss';
2

Set up the theme provider

Wrap your application with GrauityThemeProvider to enable theming:
App.tsx
import React, { useState } from 'react';
import { GrauityThemeProvider } from '@newtonschool/grauity';
import UserProfile from './UserProfile';

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <GrauityThemeProvider rootThemeScopeTheme={theme}>
      <div style={{ padding: '40px', minHeight: '100vh' }}>
        <UserProfile onThemeToggle={() => 
          setTheme(theme === 'light' ? 'dark' : 'light')
        } />
      </div>
    </GrauityThemeProvider>
  );
}

export default App;
The rootThemeScopeTheme prop accepts "light" or "dark" and controls the global theme.
3

Create your first component

Now let’s build a user profile card using Grauity components:
UserProfile.tsx
import React, { useState } from 'react';
import {
  NSButton,
  NSTextField,
  NSTypography,
  NSIcon,
  NSTag,
  BUTTON_VARIANTS_ENUM,
} from '@newtonschool/grauity';

interface UserProfileProps {
  onThemeToggle: () => void;
}

function UserProfile({ onThemeToggle }: UserProfileProps) {
  const [name, setName] = useState('Jane Cooper');
  const [email, setEmail] = useState('[email protected]');
  const [isEditing, setIsEditing] = useState(false);

  const handleSave = () => {
    setIsEditing(false);
    // Save logic here
    console.log('Saved:', { name, email });
  };

  return (
    <div
      style={{
        maxWidth: '600px',
        margin: '0 auto',
        padding: '32px',
        borderRadius: '12px',
        backgroundColor: 'var(--color-background-surface)',
        boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
      }}
    >
      {/* Header with theme toggle */}
      <div
        style={{
          display: 'flex',
          justifyContent: 'space-between',
          alignItems: 'center',
          marginBottom: '24px',
        }}
      >
        <NSTypography variant="heading-2" as="h2">
          User Profile
        </NSTypography>
        
        <NSButton
          variant="tertiary"
          icon="moon"
          onClick={onThemeToggle}
          tooltip="Toggle theme"
        >
          Toggle Theme
        </NSButton>
      </div>

      {/* Avatar section */}
      <div
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: '16px',
          marginBottom: '24px',
        }}
      >
        <div
          style={{
            width: '80px',
            height: '80px',
            borderRadius: '50%',
            backgroundColor: 'var(--color-brand-base)',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <NSIcon name="user" size="40" color="white" />
        </div>
        
        <div>
          <NSTypography variant="heading-3" as="h3">
            {name}
          </NSTypography>
          <NSTypography variant="body-1" as="p">
            {email}
          </NSTypography>
          <div style={{ marginTop: '8px' }}>
            <NSTag>Premium Member</NSTag>
          </div>
        </div>
      </div>

      {/* Editable fields */}
      {isEditing ? (
        <div style={{ marginBottom: '24px' }}>
          <NSTextField
            name="name"
            label="Full name"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="Enter your name"
            isRequired
            style={{ marginBottom: '16px' }}
          />
          
          <NSTextField
            name="email"
            label="Email address"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="Enter your email"
            isRequired
            adornments={{
              start: <NSIcon name="mail" color="currentColor" />,
            }}
          />
        </div>
      ) : (
        <div style={{ marginBottom: '24px' }}>
          <NSTypography variant="label" as="label">
            Contact Information
          </NSTypography>
          <NSTypography variant="body-2" as="p">
            You can edit your profile information by clicking the edit button below.
          </NSTypography>
        </div>
      )}

      {/* Action buttons */}
      <div style={{ display: 'flex', gap: '12px' }}>
        {isEditing ? (
          <>
            <NSButton
              variant="primary"
              color="brand"
              icon="check"
              onClick={handleSave}
              fullWidth
            >
              Save Changes
            </NSButton>
            
            <NSButton
              variant="secondary"
              color="neutral"
              onClick={() => setIsEditing(false)}
            >
              Cancel
            </NSButton>
          </>
        ) : (
          <>
            <NSButton
              variant="primary"
              color="brand"
              icon="edit"
              onClick={() => setIsEditing(true)}
              fullWidth
            >
              Edit Profile
            </NSButton>
            
            <NSButton
              variant="secondary"
              color="error"
              icon="trash"
            >
              Delete
            </NSButton>
          </>
        )}
      </div>
    </div>
  );
}

export default UserProfile;
Notice how we’re using CSS variables like var(--color-background-surface) and var(--color-brand-base). These are provided by the theme system and automatically update when you switch themes.
4

Run your application

Start your development server and you should see your user profile card:
npm run dev
# or
yarn dev
# or
pnpm dev
Try clicking the “Toggle Theme” button to switch between light and dark modes!

Understanding the components

Let’s break down the Grauity components used in this example:

NSButton

The button component supports multiple variants and colors:
<NSButton
  variant="primary"     // primary | secondary | tertiary | text
  color="brand"         // brand | neutral | error | success | warning
  size="medium"         // extra-small | small | medium | large | extra-large
  icon="check"          // Icon name from Grauity icon set
  iconPosition="left"   // left | right
  fullWidth={false}     // Make button full width
  disabled={false}      // Disable button
  loading={false}       // Show loading spinner
  onClick={() => {}}
>
  Button Text
</NSButton>

Button variants

  • primary - Solid background with white text
  • secondary - Outlined with transparent background
  • tertiary - Borderless with transparent background
  • text - Text only with no padding

NSTextField

The text field component provides a full-featured input with validation:
<NSTextField
  name="email"
  label="Email address"
  placeholder="Enter your email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  type="email"           // text | email | password | number | etc.
  isRequired={true}
  isDisabled={false}
  isReadOnly={false}
  size="medium"          // small | medium | large
  errorMessage="This field is required"
  helpMessage="We'll never share your email"
  maxLength={100}
  adornments={{
    start: <NSIcon name="mail" />,
    end: <NSButton size="small">Send</NSButton>,
  }}
/>
Text fields support adornments on both sides, allowing you to add icons, text, or even buttons inside the input.

NSTypography

The typography component provides consistent text styling:
<NSTypography
  variant="heading-1"    // heading-1 to heading-6, body-1, body-2, label, etc.
  as="h1"                // HTML element to render
  color="inherit"        // Any CSS color or "inherit"
>
  Text content
</NSTypography>

NSIcon

The icon component renders icons from the Grauity icon font:
<NSIcon
  name="check-circle"    // Icon name from icon set
  size="24"              // Size in pixels: 16, 20, 24, 32, 40, 48, 64
  color="currentColor"   // Any CSS color
  loading={false}        // Show loading animation
/>

NSTag

Simple tag component for labels and badges:
<NSTag>
  Premium Member
</NSTag>

Component props and TypeScript

Grauity provides full TypeScript support with exported types:
import {
  NSButton,
  NSTextField,
  ButtonProps,
  TextFieldProps,
  BUTTON_VARIANTS_ENUM,
  BUTTON_SIZES_ENUM,
} from '@newtonschool/grauity';

// Use types in your components
const MyButton: React.FC<ButtonProps> = (props) => {
  return <NSButton {...props} />;
};

// Use enums for type-safe values
const variant = BUTTON_VARIANTS_ENUM.PRIMARY; // "primary"
const size = BUTTON_SIZES_ENUM.MEDIUM;        // "medium"
Import types alongside components for full type safety and autocomplete support in your IDE.

Using theme variables

Grauity provides CSS variables for colors, spacing, and other design tokens:
<div
  style={{
    // Colors
    backgroundColor: 'var(--color-background-surface)',
    color: 'var(--color-text-primary)',
    borderColor: 'var(--color-border-default)',
    
    // Spacing
    padding: 'var(--spacing-16px)',
    margin: 'var(--spacing-8px)',
    gap: 'var(--spacing-12px)',
    
    // Border radius
    borderRadius: 'var(--corner-radius-8px)',
    
    // Typography
    fontSize: 'var(--font-size-14px)',
    lineHeight: 'var(--line-height-20px)',
  }}
>
  Content
</div>
These variables automatically update when you switch themes using NSThemeScope or GrauityThemeProvider.

Next steps

Explore components

Browse the full component library with live examples

Form management

Learn about the powerful useNSForm hook

Theming guide

Deep dive into advanced theming with NSThemeScope

Icon library

Explore all available icons

Common patterns

Form with validation

import { useState } from 'react';
import { NSTextField, NSButton } from '@newtonschool/grauity';

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [errors, setErrors] = useState({ email: '', password: '' });

  const handleSubmit = () => {
    const newErrors = { email: '', password: '' };
    
    if (!email) newErrors.email = 'Email is required';
    if (!password) newErrors.password = 'Password is required';
    
    setErrors(newErrors);
    
    if (!newErrors.email && !newErrors.password) {
      // Submit form
      console.log('Form submitted:', { email, password });
    }
  };

  return (
    <div style={{ maxWidth: '400px' }}>
      <NSTextField
        name="email"
        label="Email"
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        errorMessage={errors.email}
        isRequired
      />
      
      <NSTextField
        name="password"
        label="Password"
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        errorMessage={errors.password}
        isRequired
        style={{ marginTop: '16px' }}
      />
      
      <NSButton
        variant="primary"
        color="brand"
        fullWidth
        onClick={handleSubmit}
        style={{ marginTop: '24px' }}
      >
        Sign In
      </NSButton>
    </div>
  );
}
import { useState } from 'react';
import { NSModal, NSButton, NSTypography } from '@newtonschool/grauity';

function ModalExample() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <NSButton onClick={() => setIsOpen(true)}>
        Open Modal
      </NSButton>
      
      <NSModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        title="Confirm Action"
      >
        <NSTypography variant="body-1">
          Are you sure you want to proceed with this action?
        </NSTypography>
        
        <div style={{ display: 'flex', gap: '12px', marginTop: '24px' }}>
          <NSButton
            variant="primary"
            color="brand"
            onClick={() => {
              console.log('Confirmed');
              setIsOpen(false);
            }}
          >
            Confirm
          </NSButton>
          
          <NSButton
            variant="secondary"
            onClick={() => setIsOpen(false)}
          >
            Cancel
          </NSButton>
        </div>
      </NSModal>
    </>
  );
}

Loading states

import { NSButton, NSPlaceholder } from '@newtonschool/grauity';

function LoadingExample() {
  const [isLoading, setIsLoading] = useState(false);

  const handleClick = async () => {
    setIsLoading(true);
    // Simulate API call
    await new Promise(resolve => setTimeout(resolve, 2000));
    setIsLoading(false);
  };

  return (
    <NSButton
      variant="primary"
      loading={isLoading}
      onClick={handleClick}
    >
      {isLoading ? 'Loading...' : 'Click Me'}
    </NSButton>
  );
}

Troubleshooting

Make sure you’ve wrapped your app with GrauityThemeProvider:
<GrauityThemeProvider rootThemeScopeTheme="light">
  <App />
</GrauityThemeProvider>
Verify you’ve imported the icon CSS in your root stylesheet:
@import '~@newtonschool/grauity/dist/css/index.scss';
Make sure you’re importing the correct types:
import { NSButton, type ButtonProps } from '@newtonschool/grauity';
Ensure you’re updating the rootThemeScopeTheme prop when changing themes:
const [theme, setTheme] = useState('light');

<GrauityThemeProvider rootThemeScopeTheme={theme}>
  {/* Your app */}
</GrauityThemeProvider>
Need more help? Check out the full documentation or open an issue on GitHub.

Build docs developers (and LLMs) love