ArtisTree is a customizable landing page for music artists. It works like a modern “link-in-bio” for social networks: centralizing links to your platforms (Spotify, Apple Music, YouTube, etc.), upcoming shows, releases, and booking contacts.
Built with Astro + TailwindCSS v4, and designed to be easily configured via a single file: src/data/site.json.
This project uses pnpm, but you can use npm without issues. The official lockfile is pnpm-lock.yaml.
-
Clone the repository
git clone https://github.com/francosuarez-dev/artistree.git cd artistree -
Install dependencies
pnpm install
-
Start the development server
pnpm dev
The site will be available at
http://localhost:4321 -
Production build
pnpm build pnpm preview
All configuration is centralized in:
src/
data/
site.json ← edit everything here
Advanced alternative: if you want Astro’s optimized image imports, you can use
src/data/site.tsand export a typed object (see the Images section).
- Defined in
config.theme. - Valid values:
light | dark | violet | blue | green | red | pink | orange. - The layout applies
data-theme={config.theme}, and Tailwind v4 picks colors/gradients fromglobals.css.
-
Defined in
config.font. -
Valid values:
modern | elegant | grotesk. -
The layout applies
data-font={config.font}on<html>and styles are sourced fromglobals.css. -
Each preset automatically loads its typefaces from Google Fonts:
- modern → Inter (text and headings)
- elegant → Inter (text) + Playfair Display (headings)
- grotesk → Poppins (text and headings)
You can add more presets by editing the
FONT_LINKSmap inLayout.astroand the:root[data-font="..."]blocks inglobals.css.
name,description,bio→ free-form strings.picture→ see Images below.
- Supported keys (you can add more):
instagram, facebook, x, spotify, applemusic, tidal, youtube, whatsapp, email, website - The UI maps each key to an icon (yours, in
src/icons). - Only entries with a URL/value are rendered.
Examples of values:
email:"mailto:contact@..."website:"https://yoursite.com"whatsapp:"+5491123456789"(the UI may convert tohttps://wa.me/...if implemented that way)
- Contact section for bookings.
- If there is
email→ “Email” button (mailto:). - If there is
phone→ “Phone” button (tel:). - If both exist → both are shown.
"booking": { "email": "booking@...", "phone": "+54 911 2345 6789" }Fields per show:
title(optional, informational)date(ISOYYYY-MM-DD) → formatted with localees-ARand displayed in a calendar-style card (day / date / month)city,venue→ textcountry→ ISO 3166-1 alpha-2 code (AR,ES,US…) Used with flag-icons:<span class="fi fi-ar"></span>tickets(optional) +available(boolean):tickets+available: true→ Tickets buttontickets+available: false→ Sold Out (no link)- without
tickets→ no action is shown
The shows list filters future dates and sorts ascending by date.
Each release:
title(string)album(string)releaseDate(ISOYYYY-MM-DD) → used for sorting (desc)cover(string) → image pathlinks(object):- typical keys:
spotify,applemusic,deezer,tidal,youtube, etc. - "Featured" behavior: the most recent (by
releaseDate) is shown first; if it also haslinks.youtube, the video is embedded instead of the cover (with a fallback “Open on YouTube” if embedding isn’t available on mobile) - The rest of the releases are shown as regular cards with cover + platform icons
- typical keys:
In addition to basic social networks (socials), you can define a list of custom links in site.json:
"links": [
{
"label": "Personal Instagram",
"description": "Personal Instagram profile",
"url": "https://instagram.com/user",
"icon": "website"
},
{
"label": "Label Instagram",
"description": "Record label Instagram profile",
"url": "https://instagram.com/label",
"icon": "instagram",
"cover": "./img/profile.jpeg"
},
{
"label": "YouTube",
"description": "Artist YouTube channel",
"url": "https://www.youtube.com/channel/...",
"icon": "youtube"
}
]
- label: Name of the link to display
- description: Short descriptive text
- url: Full URL of the link
- icon: Icon name (e.g., instagram, youtube, website, tiktok, etc.)
- cover (optional): Custom image
- If
coveris present, it replaces the icon
You have two options:
- Place images in
public/covers/... - Use absolute paths in JSON:
"cover": "/covers/noche-eterna.jpg","picture": "/covers/profile.jpeg"
- Replace
src/data/site.jsonwithsrc/data/site.ts - Import covers so Astro generates optimized URLs:
// src/data/site.ts
import cover1 from "../assets/covers/cover1.jpeg";
import profile from "../assets/covers/profile.jpeg";
export default {
artist: { picture: profile },
releases: [{ title: "Track", cover: cover1 /* ... */ }],
};- Dates (
date,releaseDate): use ISOYYYY-MM-DD - Country (
countryin shows): use ISO alpha-2 (e.g.,AR,ES,US) - Links:
emailasmailto:...phoneas a readable number; the UI trims spaces fortel:youtubecan bewatch?v=…,youtu.be/…orshorts/…(the parser extracts the ID)
- Theme (
config.theme): use one of those defined inglobals.css
config.theme→<html data-theme="...">(applies theme colors/gradients)artist.*→ header/profilesocials→SocialIconsmaps key → icon and renders only those presentbooking→ shows Email / Phone buttons as availableshows→ calendar card + flag (flag-icons) + Tickets/Sold Out actionreleases→ sorted DESC byreleaseDate; the first is featured and, iflinks.youtubeexists, the video is embedded; the rest in a grid
- Is
config.themea valid one? - Do images point to
/covers/...inpublic/or are you usingsite.tswith imports? - Is each show’s
countrya 2-letter ISO code? - Dates in
YYYY-MM-DDformat? - Does
releases[0](the newest) havelinks.youtubeif you want the embedded video?
- The flag doesn’t appear: ensure
countryis ISO alpha-2 (e.g.,BR, notBRA). - YouTube embed says “not available” on mobile: some music videos block embedding. The UI shows an “Open on YouTube” button as a fallback.
- I want to add another social platform: create the
.astroicon, add it to theSocialIconsmap, and add the key undersocialsin the JSON.

{ "config": { "title": "Artistree Landing", "description": "Landing built with Astro", "theme": "violet", // one of: light | dark | violet | blue | green | red | pink | orange "copyright": "© 2025 ..." }, "artist": { "name": "Franco Suárez", "description": "Singer and songwriter...", "bio": "Short text...", "picture": "/covers/profile.jpeg" // see Images section }, "socials": { "instagram": "https://instagram.com/...", "facebook": "https://facebook.com/...", "x": "https://x.com/...", "youtube": "https://youtube.com/..." // optional: spotify, applemusic, tidal, whatsapp, email, website ... }, "booking": { "email": "booking@franco.com", "phone": "+54 911 2345 6789" }, "shows": [ { "title": "Show name", "date": "2025-09-12", // ISO YYYY-MM-DD "city": "Córdoba", "country": "AR", // ISO 3166-1 alpha-2 → renders the flag "venue": "Club Paraguay", "tickets": "https://tickets...", // optional "available": true // if false → shows “Sold Out” } ], "releases": [ { "title": "Noche Eterna", "album": "Noche Eterna Album", "releaseDate": "2025-08-15", // ISO YYYY-MM-DD (used for sorting) "cover": "/covers/cover1.jpeg", "links": { "spotify": "https://open.spotify.com/track/...", "applemusic": "https://music.apple.com/...", "youtube": "https://www.youtube.com/watch?v=XXXXXXXXXXX" } } ] }