
Stelut's first porfolio
This is the first project made with React, an adventure that marked my start in this ecosystem. Today, the project has evolved and has been updated to the latest versions of React to keep it relevant and functional. The source code is available on GitHub for anyone who wants to browse or learn from it.
At its inception, the application was conceived as a static page and was hosted on GitHub Pages. Although static, React already played a role, mainly at a demo level, to manage the navigation between the different sections of the portfolio and to implement a language switching functionality (Spanish/English).
In that first stage, resources such as images or project data were integrated directly into the code (‘hardcoded’), without relying on external CDNs or APIs, reflecting the initial simplicity of the project. I keep it fondly as an archive of my first steps.
An interesting aspect from the beginning was its conception as a PWA (Progressive Web App), which allowed it to be installed on mobile or desktop devices for a more direct access, offering an experience closer to a native application.
Stack and tools
-
React: used in a basic way, without advanced router or global state.
-
Vite: in later versions, I migrated to Vite to improve the development experience.
-
Pure CSS: instead of using Tailwind or frameworks, I used basic handwritten styles.
-
PWA: the app is ready to install as a native application (Web App Manifest + Service Worker).
-
GitHub Pages: the site was originally hosted on GitHub Pages because of its static nature.
Functionalities and features
Navigation
I implemented a SPA (Single Page Application) navigation system using hash paths (e.g., #/home, #/about).
Multilanguage
Allowed the user to switch between Spanish (es) and English (en), displaying the content in the language of their choice. Used a context to switch languages:
import React, { createContext, useState, useEffect } from 'react';
export const LanguageContext = createContext();
export const LanguageProvider = ({ children }) => {
const browserLanguage = navigator.language || navigator.userLanguage;
const defaultLanguage = (browserLanguage.includes('es') || browserLanguage.includes('en'))
? browserLanguage.slice(0, 2)
: 'en';
const [language, setLanguage] = useState(defaultLanguage);
const [translations, setTranslations] = useState({});
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(true);
fetch(`/local/${language}/translation.json`)
.then(response => response.json())
.then(data => {
setTimeout(() => {
setTranslations(data);
setLoading(false);
}, 150);
});
}, [language]);
return (
<LanguageContext.Provider value={{ language, setLanguage, translations, isLoading }}>
{!isLoading ? children : null}
</LanguageContext.Provider>
);
};
Later I wrapped it in index.jsx
:
<LanguageProvider>
<App />
</LanguageProvider>
And in the Navbar.jsx
it simply did this to change the language:
const { language, setLanguage, translations } = useContext(LanguageContext);
const handleLanguageChange = (event) => {
setLanguage(event.target.value);
};
The languages were preloaded as static resources in públic, src/public/local/en/translation.json
and .../en/translation.json
respectively, there I had much of the page.
User experience
The project included several smooth animations written in pure CSS, which improved the visual interaction when scrolling or navigating between sections. The design tried to be adaptive: without being completely responsive, it was taken care that at least on mobile the structure did not break, adjusting acceptably to smaller screens.
Contact
The contact form was by phone number/email, plus there was a library to send forms, formsubmit, implemented in this way:
<form action="https://formsubmit.co/test" method="POST">
<div className="fields">
<div className="field name">
<input type="text" name="name" placeholder="Nombre" required />
</div>
<div className="field email">
<input type="email" name="email" placeholder="E-Mail" required />
</div>
</div>
<div className="field">
<input type="text" name="_subject" placeholder="Asunto" required />
</div>
<div className="field textarea">
<textarea cols="30" rows="10" name="message" placeholder="Describe el asunto..."
required></textarea>
</div>
<div className="button">
<button type="submit">Enviar mensaje</button>
</div>
</form>
Conclusion
Finally, all the content was embedded directly in the frontend. There was no API, CMS or backend. Images, text and data were ‘hardcoded’, which simplified the initial development and made it ideal for understanding how to build a static site with React.
References
Other libraries involved/intervened:
- typed.js, for typing animation.
- Formsubmit for sending forms.