Starter Template

Copy. Paste. Done.

Every snippet below is real, working HTML you can paste directly into your Django template. Each one has a live preview on the left and the exact code on the right — just copy it. Set up base.html once in the Base Setup section and every component on every page just works.

Copy & Paste No Build Step Production Ready All Components

Base Setup

Add these lines to base.html once. Every component becomes available on every page automatically — no per-page imports needed.

Put the <script> tags at the very bottom of <body>, after your content blocks. Use so child templates can add their own scripts without breaking load order.

<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Your theme -->
  <link rel="stylesheet" href="/static/resources/css/style.css" />

  <!-- Phosphor Icons (used by list-tile, buttons) -->
  <link rel="stylesheet"
       href="https://cdnjs.cloudflare.com/ajax/libs/phosphor-icons/2.0.2/phosphor.min.css" />

  <!-- Quill.js -->
  <link
    rel="stylesheet"
    href="https://cdn.quilljs.com/1.3.6/quill.snow.css"
  />
</head>
<body>

<div data-barba="wrapper">
  <div data-barba="container" data-barba-namespace="default">
      
      <smart-toast position="top-right" max="5"><smart-toast position="top-right" max="5"></smart-toast>
      <smart-modal></smart-modal>
      <smart-loader type="overlay"><smart-loader type="overlay"></smart-loader>
      <!-- Your Content Goes here -->

      <!-- Your Content Ends here -->
    </div>
  </div>
  <smart-motion type="panel" duration="400"></smart-motion>
  <smart-effects auto></smart-effects>



  <!-- SmartComponents -->
  <script type="module" src="/static/resources/js/components/smart_core.js"></script>
  <script type="module" src="/static/resources/js/components/smart_motion.js"></script>
  <script type="module" src="/static/resources/js/components/smart_effect.js"></script>
  <script type="module" src="/static/resources/js/components/input.js"></script>
  <script type="module" src="/static/resources/js/components/smart_search_input.js"></script>
  <script type="module" src="/static/resources/js/components/smart_button.js"></script>
  <script type="module" src="/static/resources/js/components/button.js"></script>
  <script type="module" src="/static/resources/js/components/smart_list_tile.js"></script>
  <script type="module" src="/static/resources/js/components/rich_text_input.js"></script>
  <script type="module" src="/static/resources/js/components/smart_table.js"></script>
  <script type="module" src="/static/resources/js/components/smart_image.js"></script>
  <script type="module" src="/static/resources/js/components/smart_form.js"></script>
  <script type="module" src="/static/resources/js/components/smart_permission.js"></script>
  <script type="module" src="/static/resources/js/components/smart_chart.js"></script>
  
  <!-- Quill.js  (For SmartQuill) -->
  <script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
  
  <!-- Chart.js and ApexCharts (For SmartChart) -->
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>

</body>
</html>

SmartInput

One tag covers all input types. Validation, labels, and error states are built-in — no JS needed.

text · email · password · number · tel · url
<smart-input type="text"
  name="full_name"
  label="Full Name"
  placeholder="John Doe"
  required
></smart-input>

<smart-input type="email"
  name="email"
  label="Email"
  placeholder="you@example.com"
  required
></smart-input>

<smart-input type="password"
  name="password"
  label="Password"
  placeholder="Min 8 characters"
  required
></smart-input>

<!-- Also works: type="number" "tel" "url" -->
<smart-input type="number"
  name="age"
  label="Age"
  placeholder="25"
></smart-input>
select — static options · multi · from URL
<!-- Static options -->
<smart-input type="select"
  name="status"
  label="Status"
  value="pending"
  data-onchange="handleStatusChange"
  data-options='[
    {"id":"pending",  "name":"Pending"},
    {"id":"active",   "name":"Active"},
    {"id":"inactive", "name":"Inactive"}
  ]'
  required
></smart-input>

<!-- Multi-select -->
<smart-input type="select"
  name="tags"
  label="Tags"
  multiple
  data-options='[
    {"id":"django","name":"Django"},
    {"id":"python","name":"Python"}
  ]'
></smart-input>

<!-- Load options from API endpoint -->
<smart-input type="select"
  name="user"
  label="Assign User"
  data-url="/api/users/"
  required
></smart-input>

<script>
function handleStatusChange(event) {
  console.log('Status:', event.target.value);
}
</script>
datepicker · file · textarea
<!-- Datepicker with min / max -->
<smart-input type="datepicker"
  name="birthdate"
  label="Birth Date"
  value="15-08-2000"
  min-date="01-01-1970"
  max-date="31-12-2010"
></smart-input>

<!-- File — allowed-types: documents images
     videos spreadsheets archives audio -->
<smart-input type="file"
  name="documents"
  label="Upload Documents"
  allowed-types="documents"
  max-size="5"
  max-files="3"
></smart-input>

<!-- Images up to 10 MB, up to 5 files -->
<smart-input type="file"
  name="images"
  label="Upload Images"
  allowed-types="images"
  max-size="10"
  max-files="5"
></smart-input>

<!-- Textarea -->
<smart-input type="textarea"
  name="message"
  label="Message"
  rows="4"
  placeholder="Write here..."
  required
></smart-input>
checkbox · switch · radio
<!-- Checkbox -->
<smart-input type="checkbox"
  name="agree"
  label="I agree to the terms"
  value="true"
  required
></smart-input>

<!-- Switch (is-big = large toggle) -->
<smart-input type="switch"
  name="notifications"
  label="Enable Notifications"
  value="true"
  is-big
></smart-input>

<!-- Radio group -->
<smart-input type="radio"
  name="priority"
  label="Priority"
  value="medium"
  data-options='[
    {"id":"low",    "name":"Low"},
    {"id":"medium", "name":"Medium"},
    {"id":"high",   "name":"High"}
  ]'
  required
></smart-input>

SmartSearchInput

Type-to-search with AJAX, debounce, badge display, and keyboard nav. Point search-url at any endpoint returning [{id, name}].

async search from API

Type 2+ chars to search books

<smart-search-input
  label="Assign Lessons"
  placeholder="Search and add lessons..."
  search-url="/api/lessons/"
  input-name="lesson_ids"
  min-chars="2"
  max-results="8"
  badge-variant="secondary"
  show-badges
  allow-remove
  data-onselect="onItemSelect"
  data-onremove="onItemRemove"
></smart-search-input>

# Django: endpoint returns flat array
def search_lessons(request):
    q = request.GET.get('q', '')
    qs = Lesson.objects.filter(
        name__icontains=q
    ).values('id', 'name')[:10]
    return JsonResponse(list(qs), safe=False)

<script>
function onItemSelect(item, all) {
  console.log('Added:', item.name);
}
function onItemRemove(item, all) {
  console.log('Removed:', item.name);
}
</script>
pre-selected — for edit forms
<smart-search-input
  label="Team Members"
  placeholder="Search to add more..."
  search-url="/api/users/"
  input-name="member_ids"
  badge-variant="primary"
  show-badges
  allow-remove
  allow-edit
  edit-url-template="/admin/users/{id}/"
  pre-selected=''
></smart-search-input>

<!-- Read IDs back via public API -->
<script>
const el = document.querySelector(
  'smart-search-input'
);
console.log(el.getSelectedIds());
// → ["1", "2"]
</script>
local data — no API needed

Type 1+ char — filters local array

<smart-search-input
  label="Skills"
  placeholder="Type to filter..."
  input-name="skill_ids"
  min-chars="1"
  badge-variant="success"
  show-badges
  allow-remove
  data-onsearch="localSkillSearch"
></smart-search-input>

<script>
// Return [{id, name}] — sync or async
const SKILLS = [
  {id:1,name:"Python"},
  {id:2,name:"Django"},
  {id:3,name:"JavaScript"},
  {id:4,name:"React"},
  {id:5,name:"PostgreSQL"},
  {id:6,name:"Docker"},
];
function localSkillSearch(query) {
  return SKILLS.filter(s =>
    s.name.toLowerCase()
      .includes(query.toLowerCase())
  );
}
</script>

SmartQuill

Rich text editor with form integration. The HTML output lands in a hidden input — works with any Django view.

<form id="article-form">
  

  <smart-quill
    name="body"
    label="Article Body"
    placeholder="Write your content..."
    required
  ></smart-quill>

  <custom-button
    label="Publish Article"
    form-id="article-form"
    post="/api/articles/"
    buttontype="success"
    showspinner="true"
  ></custom-button>
</form>

# Django: request.POST['body']
# → "<p>Hello <strong>world</strong></p>"

CustomButton & SmartButton

AJAX-powered buttons. Every behaviour — spinner, confirm modal, toast — is just an attribute.

custom-button — submit · delete · export
<!-- AJAX submit with spinner -->
<custom-button
  label="Save Changes"
  form-id="my-form"
  post="/api/save/"
  buttontype="success"
  showspinner="true"
></custom-button>

<!-- Delete with styled confirm modal -->
<custom-button
  label="Delete Record"
  post="/api/items//delete/"
  buttontype="danger"
  confirm-title="Delete this record?"
  confirm-message="This cannot be undone."
></custom-button>

<!-- Info / export style -->
<custom-button
  label="Export Data"
  post="/api/export/"
  buttontype="info"
  showspinner="true"
></custom-button>
smart-button — icon-only compact variant
<!-- Icon-only button (uses Phosphor icons) -->
<smart-button
  icon="floppy-disk"
  form-id="my-form"
  post="/api/save/"
  text="Save"
  showspinner="true"
  buttontype="primary"
></smart-button>

<smart-button
  icon="pencil"
  post="/api/edit//"
  text="Edit"
  buttontype="secondary"
></smart-button>

<smart-button
  icon="trash"
  post="/api/delete//"
  text="Delete"
  buttontype="danger"
></smart-button>

SmartListTile

Sidebar nav, step wizards, menu lists. Click any tile below — the active state switches live.

<!-- active tile -->
<smart-list-tile
  id="tile-dashboard"
  leading-icon="ph ph-house"
  title="Dashboard"
  subtitle="Overview & analytics"
  active
  clickable
  trailing-icon="ph ph-caret-right"
  onclick="activateTile('tile-dashboard')"
></smart-list-tile>

<!-- regular tile -->
<smart-list-tile
  id="tile-users"
  leading-icon="ph ph-users"
  title="Users"
  subtitle="Manage team"
  clickable
  trailing-icon="ph ph-caret-right"
  onclick="activateTile('tile-users')"
></smart-list-tile>

<!-- disabled tile -->
<smart-list-tile
  leading-icon="ph ph-lock"
  title="Billing"
  subtitle="Upgrade to access"
  disabled
  trailing-icon="ph ph-lock-simple"
></smart-list-tile>

<!-- Active-state switcher JS -->
<script>
function activateTile(id) {
  document.querySelectorAll('smart-list-tile')
    .forEach(t => t.removeAttribute('active'));
  document.getElementById(id)
    .setAttribute('active', '');
}
</script>

SmartImage

Lazy load with skeleton. Every visual feature is a boolean attribute — hover-zoom, lightbox, fallback, rounded, circle.

shimmer · hover-zoom · click-preview · caption
<!-- Shimmer + hover-zoom + click lightbox -->
<smart-image
  src=""
  width="300"
  height="200"
  animation-type="shimmer"
  rounded
  hover-zoom
  click-preview
  caption=""
></smart-image>

<!-- Fluid 16:9 banner -->
<smart-image
  src=""
  aspect-ratio="16/9"
  style="width:100%"
  fit="cover"
  rounded
></smart-image>
circle avatar · spinner skeleton · fallback on error
circle
fallback-src
<!-- Circle avatar with spinner skeleton -->
<smart-image
  src=""
  width="60"
  height="60"
  animation-type="spinner"
  circle
></smart-image>

<!-- Fallback shown if src fails to load -->
<smart-image
  src=""
  fallback-src="/static/img/placeholder.jpg"
  width="300"
  height="200"
  rounded
></smart-image>

SmartTable

A full sortable, searchable, paginated data table from one tag. Delete rows with one attribute. Zero JS required.

auto-detect columns from API response
<!-- Minimal: auto-detect columns -->
<smart-table
  api-url="/api/users/"
  response-map='{"dataPath":"","totalPath":""}'
  page-size="10"
  hide-id
></smart-table>

<!-- Custom columns + badge type + delete -->
<smart-table
  api-url="/api/users/"
  response-map='{"dataPath":"results",
              "totalPath":"count"}'
  columns='[
    {"field":"name",   "label":"Name"},
    {"field":"email",  "label":"Email"},
    {"field":"status", "label":"Status",
     "type":"badge"}
  ]'
  delete-api-url="/api/users"
  page-size="20"
></smart-table>

# Django DRF view (paginated response)
class UserListView(ListAPIView):
    def list(self, request):
        qs = User.objects.all()
        return Response({
            "results": UserSerializer(
                qs, many=True
            ).data,
            "count": qs.count()
        })

Full Copy-Paste Template

A complete Django template block with every component wired up. Replace the dummy URLs, option arrays, and with your own data.

💡 This assumes base.html already imports all component scripts as shown in Base Setup. Only the HTML block and page-level JS are here.


<form id="main-form">
  

  <smart-input type="text"       name="name"      label="Full Name"  required></smart-input>
  <smart-input type="email"      name="email"     label="Email"       required></smart-input>
  <smart-input type="password"   name="password"  label="Password"   required></smart-input>

  <smart-input type="select"
    name="status" label="Status"
    data-options='[{"id":"active","name":"Active"},{"id":"inactive","name":"Inactive"}]'
    required
  ></smart-input>

  <smart-input type="datepicker" name="due_date"  label="Due Date"></smart-input>

  <smart-input type="file"
    name="docs" label="Documents"
    allowed-types="documents" max-size="5" max-files="3"
  ></smart-input>

  <smart-quill  name="description" label="Description" required></smart-quill>

  <smart-input type="switch"      name="notify"     label="Send notifications" is-big></smart-input>

  <smart-search-input
    label="Assigned Users"
    placeholder="Search users..."
    search-url="/api/users/"
    input-name="user_ids"
    badge-variant="primary"
    show-badges allow-remove
    pre-selected=''
  ></smart-search-input>

  <custom-button
    label="Save"
    form-id="main-form"
    post="/api/save/"
    buttontype="success"
    showspinner="true"
  ></custom-button>

  <custom-button
    label="Delete"
    post="/api/items//delete/"
    buttontype="danger"
    confirm-title="Delete this item?"
    confirm-message="This cannot be undone."
  ></custom-button>
</form>

<!-- Data table -->
<smart-table
  api-url="/api/users/"
  response-map='{"dataPath":"results","totalPath":"count"}'
  columns='[
    {"field":"name",   "label":"Name"},
    {"field":"email",  "label":"Email"},
    {"field":"status", "label":"Status","type":"badge"}
  ]'
  delete-api-url="/api/users"
  page-size="20"
></smart-table>

<!-- Image with fallback -->
<smart-image
  src=""
  fallback-src="/static/img/placeholder.jpg"
  width="300" height="200"
  rounded hover-zoom click-preview
></smart-image>

<script>
// Read selected IDs from smart-search-input
document.getElementById('main-form')
  .addEventListener('submit', () => {
    const s = document.querySelector('smart-search-input');
    console.log('User IDs:', s.getSelectedIds());
  });
</script>