A long introduction and installing an UI library with Tailwindcss
A good website design is already half the battle. After all, if you think about it, only its work is visible. All the work that happens on the screen is not important for the end user. What is important is that in the end this work turned out to be completely useful for him, and on this functional site it was easy to read and was not an eyesore.
After reading this article you will get something like this.
Desktop version
I know from my own experience that design is a must-have these days. And for a developer, it is also important how to do this design and improve it. And I will tell you, my dear reader, do it with pure JS and CSS; this is quite a task.
Take this site, for example. It is written with pure JS (okay, also jQuery) and CSS. Supporting it is a real pain; adding some new component (like image zoom; I still haven’t done it) is a real feat.
Therefore, in this project (SearchResultParser), I will use a ready-made user interface library. I will use MaterialUI + Tailwindcss for more flexible customization of the site style without the need to get into CSS files. And we will use the Axios library for communication with the server.
It describes which files to apply this extension to.
Also, don't forget to create an input and output file. In my case, the first one is called index.css, and the other one is zero.css. The first one is in the src directory, the other one is in static/css.
In the input file, it is enough to insert several directives:
And now, for the styles to be applied, you need to run the following command every time you change the files in which you use tailwindcss, whether it's html or js. You can add --watch so you don't have to restart it every time.
I'll say right away that I won't have many routes. Because this site is primarily an application, they are also called SAAS. My SAAS will have the following routes/views:
main: The main application page. The user will spend most of the time here.
about: The page where I will tell about this project
contacts: The page with contact information
That's all, actually. Now let's add these views and routes to them.Let's add routes to Frontend/urls.py:
This is our base. We will not return to django in this article. We will layout, layout and layout again.
Developing a base website layout
Preparation for work, launching the server
To start developing and see the results of our work, you will need to run several commands in the terminal. Firstly, so that TailwindCSS can generate styles for us. Secondly, so that React has time to collect components and render them.
To generate CSS styles using tailwindcss:
npm run tailwind -i ./src/index.css -o ./static/css/zero.css –watch
Flag -i for an input file
Flat -o for an output file
You also need to specify the --watch argument so you don't have to run this command every time you change a template or script.
To compile and generate JS:
npm run dev
Here, we run the previously recorded script in package.json. Of course, you can do it without a script, like this:
npm run webpack –mode development –watch
All that remains is to launch the Django server, open a tab and start writing code.
./manage.py runserver
Working with django templates
Let's create a base template, base.html in templates/Frontend. Open it in a text editor and paste the following code:
First, we load the value of the global variable static to have access to CSS, JS, JPEG, PNG, SVG and other media files on our server.
This file, as I call them, bases, these templates are not rendered directly, their main role is to be a skeleton/foundation/base for other templates. For example, this site, where this article is hosted, has the following bases:
base.html (Basic/common interface)
base_post.html (A base for any posts on website)
base_article.html (A base for posts of article type)
base_post_list.html (A base for pagination of any posts)
And so that the template that inherits the base could modernize it and add something of its own, special blocks need to be added. In this base there are 4 of them:
head (For meta tags, styles and scripts to start in a beginning)
header (For modification of base menu)
main (For content)
scripts (Only for scripts in the end of document)
All of the above block looks and used in django templates like this:
{% block main %}
{% endblock %}
Now that we have figured out how this template works, we need to make sure that this template is inherited by the following templates:
The template for contacts.html is identical to the template written above, with the only difference being that they have different titles, canonical address and description.
You can't live on templates alone, you need React. And you need to use it carefully. What's the matter? You might have noticed that I have special elements with IDs header and footer and next to them their analogs, meta-header and meta-footer. Why did I do this? Why not render everything in one block via react?
The reason for this is how react and django render pages. If react gives rendering to the user's machine CSR, then django does it itself, on the server SSR.
So what? What difference does it make who renders what. The main thing is that they render.
There is a difference, after all. And it is especially noticeable for search engines. A search robot, a crawler, will go to a page rendered by Django and will be able to see all the links and content of the site. But if the same crawler goes to a page rendered by React, it will see nothing, will consider the page either useless or unfinished and will leave.
That is, for SEO this is critical.
And that's why I have these meta-* elements. They are rendered by django and are available to search engines. React picks up and processes these elements.
Now that we're done with HTML, let's move on to JS and React code.
Working with React elements
Let's create the necessary elements and files. We'll need 4 of them:
Header.js (hat and website’s menu)
Footer.js
MobileAppBar.js (a menu and a hat only for the mobile version of the website)
LangSwitch.js (language switcher)
Let's start with the most complex element of our site, header.js, this is its header. The code is quite voluminous, but in essence it takes the information rendered by django and forms either horizontal (Desktop version) or vertical (Mobile version) buttons from it. That's all.
Well, if this is a mobile version, it wraps these buttons in a side menu. Because I like it the most. And here is the code:
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Button from '@mui/material/Button';
import ButtonGroup from '@mui/material/ButtonGroup';
import MenuAppBar from './MobileAppBar'
import LangSwitch from './LangSwitch';
import Drawer from '@mui/material/Drawer';
import Box from '@mui/material/Box';
import AppBar from '@mui/material/AppBar';
import LinkIcon from '@mui/icons-material/Link';
import QuestionMarkIcon from '@mui/icons-material/QuestionMark';
import LoginIcon from '@mui/icons-material/Login';
export default function Header() {
const header_buttons = document.getElementById('meta-header')
const btns = []
for ( const btn of header_buttons.children){
const ref = btn.firstElementChild.getAttribute('href')
if (btn.dataset.type == 'inner-link'){
btns.push()
}
else if (btn.dataset.type == 'tutorial' ){
btns.push()
}
else{
btns.push()
}
}
if (IS_MOBILE){
const [open, setOpen] = React.useState(false);
const toggleSideMenu = (newOpen) => () => {
setOpen(newOpen)
}
return (
In this component I’m using two more, LangSwitch and MenuAppBar.
In the MobileAppBar.js:
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import MenuIcon from '@mui/icons-material/Menu';
export default function MenuAppBar({ toggleSideMenu }) {
return (
);
}
The LangSwitch component is not that big. Most of the space is taken up by the SVG image settings. It is not working now, though, i.e. it does not switch languages. But that is because we have not yet configured django for this. This will be in the future. For now, we just have a working switch.
All that remains is to consider the Footer component in Footer.js:
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Link from '@mui/material/Link'
export default function Footer() {
const ref_blk = document.getElementById('footer-in')
const ref = ref_blk.getAttribute('href')
const text_in = ref_blk.innerText
return (
{text_in}
);
}
const container = document.getElementById('ref_to_place');
const root = createRoot(container);
root.render();
Initially, I planned to add lots and lots of links there, but I got lazy, and why would there be links there? Only an extra burden on the user's perception. So I left only one link to myself)
And of course, don't forget to connect our Header and Footer components to index.js:
import Header from './components/Header';
import Footer from './components/Footer';
Developing the main page and its components
The user experience with the application
So, we are moving on to the hardest part of this article. At least, it is the biggest one. I even thought about splitting this article, but I did not do it because of the loss of integrity of article. What will the user experience with the application look like?
A random user open the website.
Click on an add button.
Selects the required engines.
Fill the text fields.
Configures the parser.
Launches it.
As a result of the work, the user will receive a link to the downloadable file.
Django application template, app.html
Now to the application and code. Let's change the app.html template a little bit so that it can be easily worked from a react component.
From the template, you can see that my application is divided into several independent parts. These are settings (id = "app_settings"), a table of queries and engines (id = "engines" + id = "queries"), utilities (id = "app_utils") and actions (id = "app_actions").
I want to note the meta-engines block. Here I manually wrote all the engines that I am going to parse, but in the future this block will be filled by django. It's just that in the future I might want to add other engines or remove old ones and it is better to do this on the server.
React components of the application
The application is divided into 4 parts + two more components:
AppSettings.js
AppUtils.js
AppActions.js
AppQueries.js
Waiter.js
Msg.js
Create them in a src/components folder and let's move on to their analysis.
AppSettings.js
AppUtils.js
AppActions.js
AppQueries.js
Waiter.js
Msg.js
This is just a group of switches with check boxes for configuring the parser. It should be noted that the selected data is saved in the so-called data attribute. So that you can easily get them from another application, AppActions.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { IconButton, Box } from '@mui/material';
import SettingsApplicationsIcon from '@mui/icons-material/SettingsApplications';
import CloseIcon from '@mui/icons-material/Close';
import Paper from '@mui/material/Paper';
import Radio from '@mui/material/Radio';
import RadioGroup from '@mui/material/RadioGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import FormLabel from '@mui/material/FormLabel';
import FormGroup from '@mui/material/FormGroup';
import Checkbox from '@mui/material/Checkbox';
// To render an actual options to choose
function SettingsContent(){
const [exportType, setExport] = React.useState('')
const [title, setTitle] = React.useState(false)
const [description, setDesr] = React.useState(false)
const [url, setUrl] = React.useState(false)
const [verbose, setVerb] = React.useState(false)
return (
Export as:{
var root = document.getElementById('export-as')
root.dataset.export = ev.target.value
setExport(ev.target.value)
}}/>} label="exel" />
{
var root = document.getElementById('export-as')
root.dataset.export = ev.target.value
setExport(ev.target.value)
}}/>} label="csv" />
{
var root = document.getElementById('export-as')
root.dataset.export = ev.target.value
setExport(ev.target.value)
}}/>} label="json" />
Save:{
setTitle(checked)
}} />} label="title" />
{
setUrl(checked)
}} />} label="url" />
{
setDesr(checked)
}} />} label="description" />
Other:{
setVerb(checked)
const console = document.getElementById('console-button')
console.classList.toggle('hidden')
}} />} label="verbose" />
)
}
export default function AppSettings(){
const [isSettings, setSettings] = React.useState(false);
// To hide and show available choises for user
const ToggleSettings = () => {
if (isSettings == true){
setSettings(false);
const set_cont = document.getElementById('settings_content')
set_cont.classList.add('hidden')
}
else{
const set_cont = document.getElementById('settings_content')
set_cont.classList.remove('hidden')
setSettings(true)
}
}
return (
{/* Icon button is gonna be changed */}
{ isSettings ?
: }
This component is implemented using this chain of actions/uis, button→bottom slider→modal window. And at each stage, requests will be made to the server to get the necessary presets.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { IconButton, Box } from '@mui/material';
import TrendingUpIcon from '@mui/icons-material/TrendingUp';
import FeaturedPlayListIcon from '@mui/icons-material/FeaturedPlayList';
import SavedSearchIcon from '@mui/icons-material/SavedSearch';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import UploadIcon from '@mui/icons-material/Upload';
import InfoIcon from '@mui/icons-material/Info';
import Drawer from '@mui/material/Drawer';
import Typography from '@mui/material/Typography';
import Modal from '@mui/material/Modal';
import Button from '@mui/material/Button';
import List from '@mui/material/List';
import ListItemText from '@mui/material/ListItemText';
import {Wait, StopWait} from './Waiter';
import DownloadIcon from '@mui/icons-material/Download';
import axios from "axios";
// To get info about preset
function onUtilsContentInfo(rec){
// Make a GET request for a specific preset
var uid = 0
Wait()
axios.get(`/api/presets/${uid}`)
.then(response => {
// Here recieve a file
StopWait('Successfully obtain info about preset.', 'success')
})
.catch(error => {
StopWait('Cant obtain info about preset. ' + error , 'error')
});
// Apply a recieved data to a poped up modal window
const utils_modal = document.getElementById('utils-modal')
const utils_modal_root = createRoot(utils_modal);
utils_modal_root.render(
Preset info
);
}
// To apply preset for current user
function onUtilsContentUpload(rec){
var uid = 0
Wait()
axios.get(`/api/presets/${uid}`)
.then(response => {
// Here recieve a file
StopWait('Successfully apply preset for user.', 'success')
})
.catch(error => {
StopWait('Cant apply preset for user. ' + error , 'error')
});
// Make a GET request for a specific preset
// Apply recieved preset for user
}
// To delete preset of current user
function onUtilsContentDelete(rec){
var uid = 0
// Make a DEL request to remove a specific preset
axios.delete(`/api/presets/${uid}`)
.then(data => {
// Here recieve a file
StopWait('Successfully delete a preset.', 'success')
Wait()
axios.get('/api/presets/')
.then(response => {
// Here recieve a file
StopWait('Successfully refreshed all users presets.', 'success')
})
.catch(error => {
StopWait('Cant refresh user presets. ' + error , 'error')
});
})
.catch(error => {
StopWait('Cant delete a data. ' + error , 'error')
});
const utils_modal = document.getElementById('utils-modal')
const utils_modal_root = createRoot(utils_modal);
utils_modal_root.render(
Deleting preset
Are you sure ?
);
}
// Wait till modal is present and ready to be interactable
// Then launch callback with args
function waitTillModalIsUp(func, args){
const observer = new MutationObserver((mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.addedNodes.length > 0){
const utils_modal = mutation.target.querySelector("#utils-modal");
if(utils_modal){
func(args)
observer.disconnect()
}
}
}
});
observer.observe(document, {subtree: true, childList: true});
}
// Popular presets, I will create them by myself
function TrendingContent( props ){
const [isModal, setModal] = React.useState(false);
// Make a POST request to collect most popular presets
Wait()
axios.get('/api/popular-presets/')
.then(response => {
// Here recieve a file
StopWait('Successfully obtain popular presets.', 'success')
})
.catch(error => {
StopWait('Cant get popular presets. ' + error , 'error')
});
const rec = {'setModal': setModal}
return (
{setModal(false)}}
>
Trending presets
{/* Template to be rendered by Django*/}
)
}
// Saved presets of user
function OwnSavesContent(props){
const [isModal, setModal] = React.useState(false);
// Make GET firts request to obtain all presets by user
Wait()
axios.get('/api/presets/')
.then(response => {
// Here recieve a file
StopWait('Successfully obtain all users presets.', 'success')
})
.catch(error => {
StopWait('Cant get presets. ' + error , 'error')
});
const rec = {'setModal': setModal}
return (
{setModal(false)}}
>
Your presets
{/* Template to be rendered by Django */}
Consists of only two buttons, saving the preset and starting the parsing. Here, we collect data from other applications, here we check them for correctness and here we send them to the server.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { IconButton, Box } from '@mui/material';
import SaveIcon from '@mui/icons-material/Save';
import NotStartedIcon from '@mui/icons-material/NotStarted';
import Typography from '@mui/material/Typography';
import Modal from '@mui/material/Modal';
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import { Wait, StopWait, Msg } from './Waiter';
import axios from "axios";
// Wait till modal is present and ready to be interactable
// Then launch callback with args
function waitTillModalIsUp(func, args){
const observer = new MutationObserver((mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.addedNodes.length > 0){
const utils_modal = mutation.target.querySelector("#utils-modal");
if(utils_modal){
func(args)
observer.disconnect()
}
}
}
});
observer.observe(document, {subtree: true, childList: true});
}
// A set of checks to be checked if user make everything is in a right way
function checkIfValidPreset(){
// Collect data
var exportAs = document.getElementById('export-as').dataset.export
var isTitle = document.getElementById('dataTitle').querySelector('input').checked
var isUrl = document.getElementById('dataUrl').querySelector('input').checked
var isDescription = document.getElementById('dataDescription').querySelector('input').checked
if (!isTitle && !isUrl && !isDescription){
Msg('You must "check" one of these: title, url, description','error')
return false
}
var queries = []
var queries_raw = document.querySelectorAll('.query')
queries_raw.forEach((que) => {
var engine = que.dataset.engine
var query = que.querySelector('input').value
queries.push({
engine: engine,
query: query
})
})
if (queries.length >= 1){
var isValid = true
queries.forEach((que) => {
if (que.query == ""){
Msg('Query string cant be empty.','error')
isValid = false
}
})
return isValid
}
else{
Msg('At least 1 query must be.','error')
return false
}
}
// Make a PUT request to save preset
function SaveRequest(req){
// Collect data
var exportAs = document.getElementById('export-as').dataset.export
var isTitle = document.getElementById('dataTitle').querySelector('input').checked
var isUrl = document.getElementById('dataUrl').querySelector('input').checked
var isDescription = document.getElementById('dataDescription').querySelector('input').checked
var queries = []
var queries_raw = document.querySelectorAll('.query')
queries_raw.forEach((que) => {
var engine = que.dataset.engine
var query = que.querySelector('input').value
queries.push({
engine: engine,
query: query
})
})
if (checkIfValidPreset()){
const actions_modal = document.getElementById('utils-modal')
const actions_modal_root = createRoot(actions_modal);
actions_modal_root.render(
Saving preset
)
}
}
//Make a POST request to server to get results
function StartParsingRequest(req){
// Collect data
var exportAs = document.getElementById('export-as').dataset.export
var isTitle = document.getElementById('dataTitle').querySelector('input').checked
var isUrl = document.getElementById('dataUrl').querySelector('input').checked
var isDescription = document.getElementById('dataDescription').querySelector('input').checked
var queries = []
var queries_raw = document.querySelectorAll('.query')
queries_raw.forEach((que) => {
var engine = que.dataset.engine
var query = que.querySelector('input').value
queries.push({
engine: engine,
query: query
})
})
if (checkIfValidPreset()){
Wait()
var form_data = new FormData();
form_data.append("exportAs", exportAs)
form_data.append("isTitle", isTitle)
form_data.append("isUrl", isUrl)
form_data.append("isDescription", isDescription)
form_data.append("queries", JSON.stringify(queries))
axios.post('/api/parse/', form_data)
.then(data => {
// Here recieve a file
StopWait('Successfully parsed a data.', 'success')
})
.catch(error => {
StopWait('Cant parse data. ' + error , 'error')
});
}
}
export default function AppActions(){
const [isModal, setModal] = React.useState(false);
const req = {'setModal': setModal}
return (
{setModal(true); waitTillModalIsUp(SaveRequest, req)}} className='w-fit'>{setModal(false)}}
>
{StartParsingRequest(req)}} className='w-fit'>
)
}
const actions_container = document.getElementById('app_actions');
const actions_root = createRoot(actions_container);
actions_root.render();
Getting engines available for parsing and creating an engine-query table. Initially, I planned to do it in such a way that the user would add an engine, then add as many queries to it as he wanted. But then I realized that all this can be implemented much more simply and through one button.
And also, you will need icons for all engines. You will need to download this archive and unpack it in the Frontend/static/img folder
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import AddBoxIcon from '@mui/icons-material/AddBox';
import { IconButton, Box } from '@mui/material';
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import TextField from '@mui/material/TextField';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import {Wait, StopWait} from './Waiter';
import Popover from '@mui/material/Popover';
import Divider from '@mui/material/Divider';
// Remove a line of query from UI
function onDeleteQuery(event){
var id = event.currentTarget.dataset.id
var elsToDelete = document.querySelectorAll('#'+id)
elsToDelete.forEach((el)=>{
el.remove()
})
}
// Compiling and inserting a choosen engine with text field
function onAddQuery(event){
var uid = 'uid_'+Math.random().toString(16).slice(2)
// Inserting engine before + button and showing up all activity buttons
var child = event.currentTarget.children[0].cloneNode(true)
child.id = uid
child.querySelectorAll('#toRemoveEngineStuff').forEach( (el) => {
el.classList.remove('hidden')
})
var deleteButton = child.querySelector('#onDeleteQuery')
deleteButton.dataset.id = uid
deleteButton.addEventListener('click', onDeleteQuery)
var parent = document.getElementById('engines_list')
parent.insertBefore(child, parent.lastChild);
var engine_name = event.currentTarget.dataset.engine_name
// Activate 'save' and 'parse' buttons
// Inserting query text field
var querCont = document.getElementById('queries_list');
var query = document.createElement("div");
query.id = uid
querCont.insertBefore(query, querCont.lastChild);
const querRoot = createRoot(query);
querRoot.render(
)
}
export default function AppQueries(){
const [anchorEngine, setAnchoreEngine] = React.useState(null)
var engine_list = []
const list = document.getElementById('meata-engines').children
for (var i = 0; i < list.length; i++){
// Find name of engine
var end = list[i].dataset.src.indexOf('.')
var start = list[i].dataset.src.lastIndexOf('/') + 1
var name = String(list[i].dataset.src).substring(start, end)
// Push engines to pop up window, for to be selected later
engine_list.push(
{
onAddQuery(event)
}}>
It exists only to show the user that the server is currently busy and needs to wait a bit. It also controls the display of messages about success or failure when the server is running.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import CircularProgress from '@mui/material/CircularProgress';
import Backdrop from '@mui/material/Backdrop';
export function Msg(msg, status){
const msg_container = document.getElementById('msg');
msg_container.classList.remove('hidden')
const msgText_container = document.getElementById('msg-text');
msgText_container.querySelector('.MuiAlert-message').innerText = msg
}
export function CloseMsg(){
const msg_container = document.getElementById('msg');
msg_container.classList.add('hidden')
const msgText_container = document.getElementById('msg-text');
msgText_container.querySelector('.MuiAlert-message').innerText = ''
}
// Show up a waiter
export function Wait(){
const waiter_container = document.getElementById('waiter');
waiter_container.classList.remove('hidden')
const header = document.getElementById('header');
header.style.zIndex = 0
}
// Hide waiter and shop up a status message
export function StopWait(msg, status){
const waiter_container = document.getElementById('waiter');
waiter_container.classList.add('hidden')
const header = document.getElementById('header');
header.style.zIndex = 1100
Msg(msg, status)
}
export default function Waiter(){
return (
)
}
const waiter_container = document.getElementById('waiter');
const waiter_root = createRoot(waiter_container);
waiter_root.render()
This file is used to prepare (draw) a certain block to be filled with information about the results of the server's work. It is controlled via the Waiter component.
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import Alert from '@mui/material/Alert';
import { CloseMsg } from './Waiter';
export default function Msg(){
return (
)
}
const msg_container = document.getElementById('msg');
const msg_root = createRoot(msg_container);
msg_root.render()
All that remains is to connect all these components to index.js:
import Header from './components/Header';
import Footer from './components/Footer';
import AppSettings from './components/AppSettings';
import AppUtils from './components/AppUtils';
import AppActions from './components/AppActions';
import AppQueries from './components/AppQueries';
import Msg from './components/Msg';
Other pages and website’s sections.
I won't cover pages like about and contacts in such detail. Why?
These are general (I would even say standard) pages. And basically they will be static, there will be no react. They do not affect the main functionality of the site in any way. And simply, what's the point of showing what I wrote there? Or what's more important, what to tell? What font do I use, or what indents do I make?) That's it.
Conclusion
In this article, I told and showed how you can make the frontend part of the site using React and Django + MaterialUI, so as not to reinvent the wheel. TailwindCSS, to gain maximum flexibility in styling page elements (okay, so as not to get into CSS files :)).
In general, the frontend has always been the hardest part of development for me, well, it's not my thing. Making something functional and working - yes, I can do that. But making it beautiful and stylish - that's where I end.
You probably know this analogy of the frontend and the backend.
Well, for me, it's the opposite. In any case, we're done with the hardest part, and it will only get easier. We'll add an interactive tutorial, support for several languages, a backend in the end, and user authentication.
If you skipped all of the above and just want a ready-to-use solution, here it is. An archive with preconfigured folder structure and precalculated dependencies. All you need to do is set up a virtual environment (install all required Python packages) for the downloaded folder and install the required NPM packages in the Frontend app.
0
0
External links
Used termins
Axios library ⟶ It is a promise-based HTTP Client for node.js and the browser. It is isomorphic (= it can run in the browser and nodejs with the same codebase). On the server-side it uses the native node.js http module, while on the client (browser) it uses XMLHttpRequests.
Modal window ⟶ A modal window, often referred to as a modal dialog or modal popup, is a graphical user interface element that appears on top of an application’s main window or web page. It typically requires the user to interact with it before they can return to the underlying content.
Client side rendering(CSR) ⟶ It is a JavaScript rendering method that uses JavaScript to render a website or application in the browser. With CSR, the processing and rendering of the content happens in the browser rather than on the server.
Javascript ⟶ Is a high-level, interpreted programming language that is commonly used for web development. It is an essential part of web applications, enabling interactive features and dynamic content on websites.
Related questions
How does prop children work?
Some components do not know their children in advance. This is especially true for components like Sidebar or Dialog, which are like a "box" into which you can put something. For such components, we recommend using a special prop children, which will pass the child elements directly to the output
What is the difference between managed and unmanaged components?
In a controlled component, each state mutation has a handler function associated with it. This makes validating or changing the input value a simple task. Uncontrolled components rely on the DOM as a data source and can be useful when integrating React with non-React code.
How can I disable the ripple effect globally?
The ripple effect is exclusively coming from the BaseButton component. You can disable the ripple effect globally by providing the corresponding value in your theme.
In this article you will understand how to add a web tutorial on a website for guests using React components. With the ability to define to which elements hints will …
I will be busy developing a new project. His name is SearchResultParser. Its essence is to parse data from the search results of various search engines, such as google, youtube, …