The working example below is derived from the answers here, here, as well as here, here and here, at which I would suggest you have a look for more details and explanation.
Sample data
data.csv
Id,name,age,height,weight
1,Alice,20,62,120.6
2,Freddie,21,74,190.6
3,Bob,17,68,120.0
Option 1 - Return modified data in a new CSV file
app.py
from fastapi import FastAPI, File, UploadFile, Request, Response, HTTPException
from fastapi.templating import Jinja2Templates
from io import BytesIO
import pandas as pd
app = FastAPI()
templates = Jinja2Templates(directory='templates')
@app.post('/upload')
def upload(file: UploadFile = File(...)):
    try:
        contents = file.file.read()
        buffer = BytesIO(contents) 
        df = pd.read_csv(buffer)
    except:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        buffer.close()
        file.file.close()
    # remove a column from the DataFrame
    df.drop('age', axis=1, inplace=True)
    
    headers = {'Content-Disposition': 'attachment; filename="modified_data.csv"'}
    return Response(df.to_csv(), headers=headers, media_type='text/csv')
    
@app.get('/')
def main(request: Request):
    return templates.TemplateResponse('index.html', {'request': request})
templates/index.html
<!DOCTYPE html>
<html>
   <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
   <body>
      <form method="post" action="/upload"  enctype="multipart/form-data">
         <label for="csvFile">Choose a CSV file</label>
         <input type="file" id="csvFile" name="file" onchange="enableSubmitBtn();"><br><br>
         <input type="submit" id="submitBtn" value="submit" disabled>
      </form>
      <script>
         function enableSubmitBtn() {
            document.getElementById('submitBtn').removeAttribute("disabled");
         }
      </script>
   </body>
</html>
Option 2 - Return modified data in a new Jinja2 Template
If you would rather like to return a new Jinja2 template with the modified data instead of a csv file as demonstrated above, you could use the below.
Method 1
Use pandas.DataFrame.to_html() to render the DataFrame as an HTML table. You could optionally use the classes parameter in to_html() function to pass a class name, or a list of names, that will be used in a style sheet in your frontend to style the table. Additionally, you could remove the border by specifying border=0 in to_html().
app.py
# ... (rest of code is same as in Option 1)
@app.post('/upload')
def upload(request: Request, file: UploadFile = File(...)):
    # ... (rest of code is same as in Option 1)
    context = {'request': request, 'table': df.to_html()}
    return templates.TemplateResponse('results.html', context)
templates/results.html
<!DOCTYPE html>
<html>
    <body>{{ table | safe }}</body>
</html>
Method 2
Use pandas.DataFrame.to_dict() to convert the DataFrame to a dictionary and return it.
app.py
# ... (rest of code is same as in Option 1)
@app.post('/upload')
def upload(request: Request, file: UploadFile = File(...)):
    # ... (rest of code is same as in Option 1)
    context = {'request': request, 'data': df.to_dict(orient='records'), 'columns': df.columns.values}
    return templates.TemplateResponse('results.html', context)
templates/results.html
<!DOCTYPE html>
<html>
    <body>
        <table style="width:50%">
            <tr>
                {% for c in columns %}<td>{{ c }}</td>{% endfor %}
            </tr>
            {% for d in data %}
                <tr>
                    {% for v in d.values() %}
                        <td>{{ v }}</td>
                    {% endfor %}
                    <br>
                </tr>
            {% endfor %}
        </table>
    </body>
</html>